summaryrefslogtreecommitdiffstats
path: root/drivers/tty/vt
diff options
context:
space:
mode:
authorSean Christopherson <seanjc@google.com>2026-09-23 09:37:21 -0700
committerPaolo Bonzini <pbonzini@redhat.com>2026-09-26 00:39:55 -0400
commit93de2a6a4b91b72607136dd656edf03fb399d27f (patch)
tree9220c76eb23cb39a146fac9692ee69afa4aa3135 /drivers/tty/vt
downloadlinux-stable-93de2a6a4b91b72607136dd656edf03fb399d27f.tar.gz
linux-stable-93de2a6a4b91b72607136dd656edf03fb399d27f.zip
KVM: SEV: Do cache maintenance on the source VM during intra-host migrationgrafted
Manually perform cache maintenance on the source VM during intra-host migration to ensure no stale data is left in CPU caches after the VM is destroyed. Because the source VM is "converted" to a non-SEV VM, KVM's memory reclaim flows won't trigger cache maintenance, e.g. when all guest memory is reclaimed in response to detaching from the mmu_notifier. Note, relying on the destination VM to do cache maintenance isn't an option as KVM doesn't require identical guest memory configurations, i.e. the source VM may have access to memory that the destination VM does not. Enforcing equivalent memory configurations is infeasible, as it would require a *deep* comparison of memslots, e.g. to verify that not only are the memslot identical, but what the memslots point at is also identical. Fixes: b56639318bb2 ("KVM: SEV: Add support for SEV intra host migration") Cc: stable@vger.kernel.org Reported-by: Stefan Teodorescu <fane@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Message-ID: <20260923163721.1584779-3-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'drivers/tty/vt')
-rw-r--r--drivers/tty/vt/.gitignore7
-rw-r--r--drivers/tty/vt/Makefile65
-rw-r--r--drivers/tty/vt/conmakehash.c287
-rw-r--r--drivers/tty/vt/consolemap.c901
-rw-r--r--drivers/tty/vt/cp437.uni292
-rw-r--r--drivers/tty/vt/defkeymap.c_shipped375
-rw-r--r--drivers/tty/vt/defkeymap.map358
-rwxr-xr-xdrivers/tty/vt/gen_ucs_fallback_table.py360
-rwxr-xr-xdrivers/tty/vt/gen_ucs_recompose_table.py257
-rwxr-xr-xdrivers/tty/vt/gen_ucs_width_table.py354
-rw-r--r--drivers/tty/vt/keyboard.c2291
-rw-r--r--drivers/tty/vt/selection.c459
-rw-r--r--drivers/tty/vt/ucs.c293
-rw-r--r--drivers/tty/vt/ucs_fallback_table.h_shipped3346
-rw-r--r--drivers/tty/vt/ucs_recompose_table.h_shipped102
-rw-r--r--drivers/tty/vt/ucs_width_table.h_shipped490
-rw-r--r--drivers/tty/vt/vc_screen.c804
-rw-r--r--drivers/tty/vt/vt.c5113
-rw-r--r--drivers/tty/vt/vt_ioctl.c1319
19 files changed, 17473 insertions, 0 deletions
diff --git a/drivers/tty/vt/.gitignore b/drivers/tty/vt/.gitignore
new file mode 100644
index 000000000..a74859bab
--- /dev/null
+++ b/drivers/tty/vt/.gitignore
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+/conmakehash
+/consolemap_deftbl.c
+/defkeymap.c
+/ucs_fallback_table.h
+/ucs_recompose_table.h
+/ucs_width_table.h
diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile
new file mode 100644
index 000000000..ae746dcde
--- /dev/null
+++ b/drivers/tty/vt/Makefile
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# 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 \
+ vt.o defkeymap.o
+obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o \
+ ucs.o
+
+# Files generated that shall be removed upon make clean
+clean-files := consolemap_deftbl.c defkeymap.c \
+ ucs_width_table.h ucs_recompose_table.h ucs_fallback_table.h
+
+hostprogs += conmakehash
+
+quiet_cmd_conmk = CONMK $@
+ cmd_conmk = $(obj)/conmakehash $< > $@
+
+$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) $(obj)/conmakehash
+ $(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 --unicode $< > $@
+
+endif
+
+$(obj)/ucs.o: $(src)/ucs.c $(obj)/ucs_width_table.h \
+ $(obj)/ucs_recompose_table.h $(obj)/ucs_fallback_table.h
+
+# You may uncomment one of those to have the UCS tables be regenerated
+# during the build process. By default the _shipped versions are used.
+#
+#GENERATE_UCS_TABLES := 1
+#GENERATE_UCS_TABLES := 2 # invokes gen_ucs_recompose_table.py with --full
+
+ifdef GENERATE_UCS_TABLES
+
+$(obj)/ucs_width_table.h: $(src)/gen_ucs_width_table.py
+ $(PYTHON3) $< -o $@
+
+ifeq ($(GENERATE_UCS_TABLES),2)
+gen_recomp_arg := --full
+else
+gen_recomp_arg :=
+endif
+
+$(obj)/ucs_recompose_table.h: $(src)/gen_ucs_recompose_table.py
+ $(PYTHON3) $< -o $@ $(gen_recomp_arg)
+
+$(obj)/ucs_fallback_table.h: $(src)/gen_ucs_fallback_table.py
+ $(PYTHON3) $< -o $@
+
+endif
diff --git a/drivers/tty/vt/conmakehash.c b/drivers/tty/vt/conmakehash.c
new file mode 100644
index 000000000..a931fcde7
--- /dev/null
+++ b/drivers/tty/vt/conmakehash.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * conmakehash.c
+ *
+ * Create arrays for initializing the kernel folded tables (using a hash
+ * table turned out to be to limiting...) Unfortunately we can't simply
+ * preinitialize the tables at compile time since kfree() cannot accept
+ * memory not allocated by kmalloc(), and doing our own memory management
+ * just for this seems like massive overkill.
+ *
+ * Copyright (C) 1995-1997 H. Peter Anvin
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MAX_FONTLEN 256
+
+typedef unsigned short unicode;
+
+static void usage(char *argv0)
+{
+ fprintf(stderr, "Usage: \n"
+ " %s chartable [hashsize] [hashstep] [maxhashlevel]\n", argv0);
+ exit(EX_USAGE);
+}
+
+static int getunicode(char **p0)
+{
+ char *p = *p0;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p != 'U' || p[1] != '+' ||
+ !isxdigit(p[2]) || !isxdigit(p[3]) || !isxdigit(p[4]) ||
+ !isxdigit(p[5]) || isxdigit(p[6]))
+ return -1;
+ *p0 = p+6;
+ return strtol(p+2,0,16);
+}
+
+unicode unitable[MAX_FONTLEN][255];
+ /* Massive overkill, but who cares? */
+int unicount[MAX_FONTLEN];
+
+static void addpair(int fp, int un)
+{
+ int i;
+
+ if ( un <= 0xfffe )
+ {
+ /* Check it isn't a duplicate */
+
+ for ( i = 0 ; i < unicount[fp] ; i++ )
+ if ( unitable[fp][i] == un )
+ return;
+
+ /* Add to list */
+
+ if ( unicount[fp] > 254 )
+ {
+ fprintf(stderr, "ERROR: Only 255 unicodes/glyph permitted!\n");
+ exit(EX_DATAERR);
+ }
+
+ unitable[fp][unicount[fp]] = un;
+ unicount[fp]++;
+ }
+
+ /* otherwise: ignore */
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *ctbl;
+ const char *tblname;
+ char buffer[65536];
+ int fontlen;
+ int i, nuni, nent;
+ int fp0, fp1, un0, un1;
+ char *p, *p1;
+
+ if ( argc < 2 || argc > 5 )
+ usage(argv[0]);
+
+ if ( !strcmp(argv[1],"-") )
+ {
+ ctbl = stdin;
+ tblname = "stdin";
+ }
+ else
+ {
+ ctbl = fopen(tblname = argv[1], "r");
+ if ( !ctbl )
+ {
+ perror(tblname);
+ exit(EX_NOINPUT);
+ }
+ }
+
+ /* For now we assume the default font is always 256 characters. */
+ fontlen = 256;
+
+ /* Initialize table */
+
+ for ( i = 0 ; i < fontlen ; i++ )
+ unicount[i] = 0;
+
+ /* Now we come to the tricky part. Parse the input table. */
+
+ while ( fgets(buffer, sizeof(buffer), ctbl) != NULL )
+ {
+ if ( (p = strchr(buffer, '\n')) != NULL )
+ *p = '\0';
+ else
+ fprintf(stderr, "%s: Warning: line too long\n", tblname);
+
+ p = buffer;
+
+/*
+ * Syntax accepted:
+ * <fontpos> <unicode> <unicode> ...
+ * <range> idem
+ * <range> <unicode range>
+ *
+ * where <range> ::= <fontpos>-<fontpos>
+ * and <unicode> ::= U+<h><h><h><h>
+ * and <h> ::= <hexadecimal digit>
+ */
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (!*p || *p == '#')
+ continue; /* skip comment or blank line */
+
+ fp0 = strtol(p, &p1, 0);
+ if (p1 == p)
+ {
+ fprintf(stderr, "Bad input line: %s\n", buffer);
+ exit(EX_DATAERR);
+ }
+ p = p1;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '-')
+ {
+ p++;
+ fp1 = strtol(p, &p1, 0);
+ if (p1 == p)
+ {
+ fprintf(stderr, "Bad input line: %s\n", buffer);
+ exit(EX_DATAERR);
+ }
+ p = p1;
+ }
+ else
+ fp1 = 0;
+
+ if ( fp0 < 0 || fp0 >= fontlen )
+ {
+ fprintf(stderr,
+ "%s: Glyph number (0x%x) larger than font length\n",
+ tblname, fp0);
+ exit(EX_DATAERR);
+ }
+ if ( fp1 && (fp1 < fp0 || fp1 >= fontlen) )
+ {
+ fprintf(stderr,
+ "%s: Bad end of range (0x%x)\n",
+ tblname, fp1);
+ exit(EX_DATAERR);
+ }
+
+ if (fp1)
+ {
+ /* we have a range; expect the word "idem" or a Unicode range of the
+ same length */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (!strncmp(p, "idem", 4))
+ {
+ for (i=fp0; i<=fp1; i++)
+ addpair(i,i);
+ p += 4;
+ }
+ else
+ {
+ un0 = getunicode(&p);
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p != '-')
+ {
+ fprintf(stderr,
+"%s: Corresponding to a range of font positions, there should be a Unicode range\n",
+ tblname);
+ exit(EX_DATAERR);
+ }
+ p++;
+ un1 = getunicode(&p);
+ if (un0 < 0 || un1 < 0)
+ {
+ fprintf(stderr,
+"%s: Bad Unicode range corresponding to font position range 0x%x-0x%x\n",
+ tblname, fp0, fp1);
+ exit(EX_DATAERR);
+ }
+ if (un1 - un0 != fp1 - fp0)
+ {
+ fprintf(stderr,
+"%s: Unicode range U+%x-U+%x not of the same length as font position range 0x%x-0x%x\n",
+ tblname, un0, un1, fp0, fp1);
+ exit(EX_DATAERR);
+ }
+ for(i=fp0; i<=fp1; i++)
+ addpair(i,un0-fp0+i);
+ }
+ }
+ else
+ {
+ /* no range; expect a list of unicode values for a single font position */
+
+ while ( (un0 = getunicode(&p)) >= 0 )
+ addpair(fp0, un0);
+ }
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p && *p != '#')
+ fprintf(stderr, "%s: trailing junk (%s) ignored\n", tblname, p);
+ }
+
+ /* Okay, we hit EOF, now output hash table */
+
+ fclose(ctbl);
+
+
+ /* Compute total size of Unicode list */
+ nuni = 0;
+ for ( i = 0 ; i < fontlen ; i++ )
+ nuni += unicount[i];
+
+ printf("\
+/*\n\
+ * Automatically generated file; Do not edit.\n\
+ */\n\
+\n\
+#include <linux/types.h>\n\
+\n\
+u8 dfont_unicount[%d] = \n\
+{\n\t", fontlen);
+
+ for ( i = 0 ; i < fontlen ; i++ )
+ {
+ printf("%3d", unicount[i]);
+ if ( i == fontlen-1 )
+ printf("\n};\n");
+ else if ( i % 8 == 7 )
+ printf(",\n\t");
+ else
+ printf(", ");
+ }
+
+ printf("\nu16 dfont_unitable[%d] = \n{\n\t", nuni);
+
+ fp0 = 0;
+ nent = 0;
+ for ( i = 0 ; i < nuni ; i++ )
+ {
+ while ( nent >= unicount[fp0] )
+ {
+ fp0++;
+ nent = 0;
+ }
+ printf("0x%04x", unitable[fp0][nent++]);
+ if ( i == nuni-1 )
+ printf("\n};\n");
+ else if ( i % 8 == 7 )
+ printf(",\n\t");
+ else
+ printf(", ");
+ }
+
+ exit(EX_OK);
+}
diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c
new file mode 100644
index 000000000..7d564341a
--- /dev/null
+++ b/drivers/tty/vt/consolemap.c
@@ -0,0 +1,901 @@
+// SPDX-License-Identifier: GPL-2.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 <jj@ultra.linux.cz>, July 1998
+ *
+ * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998
+ *
+ * In order to prevent the following circular lock dependency:
+ * &mm->mmap_lock --> cpu_hotplug.lock --> console_lock --> &mm->mmap_lock
+ *
+ * We cannot allow page fault to happen while holding the console_lock.
+ * Therefore, all the userspace copy operations have to be done outside
+ * the console_lock critical sections.
+ *
+ * As all the affected functions are all called directly from vt_ioctl(), we
+ * can allocate some small buffers directly on stack without worrying about
+ * stack overflow.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/kd.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/uaccess.h>
+#include <linux/console.h>
+#include <linux/consolemap.h>
+#include <linux/vt_kern.h>
+#include <linux/string.h>
+
+static unsigned short translations[][E_TABSZ] = {
+ /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */
+ [LAT1_MAP] = {
+ 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 */
+ [GRAF_MAP] = {
+ 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 */
+ [IBMPC_MAP] = {
+ 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 */
+ [USER_MAP] = {
+ 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 enum translation_map inv_translate[MAX_NR_CONSOLES];
+
+#define UNI_DIRS 32U
+#define UNI_DIR_ROWS 32U
+#define UNI_ROW_GLYPHS 64U
+
+#define UNI_DIR_BITS GENMASK(15, 11)
+#define UNI_ROW_BITS GENMASK(10, 6)
+#define UNI_GLYPH_BITS GENMASK( 5, 0)
+
+#define UNI_DIR(uni) FIELD_GET(UNI_DIR_BITS, (uni))
+#define UNI_ROW(uni) FIELD_GET(UNI_ROW_BITS, (uni))
+#define UNI_GLYPH(uni) FIELD_GET(UNI_GLYPH_BITS, (uni))
+
+#define UNI(dir, row, glyph) (FIELD_PREP(UNI_DIR_BITS, (dir)) | \
+ FIELD_PREP(UNI_ROW_BITS, (row)) | \
+ FIELD_PREP(UNI_GLYPH_BITS, (glyph)))
+
+/**
+ * struct uni_pagedict - unicode directory
+ *
+ * @uni_pgdir: 32*32*64 table with glyphs
+ * @refcount: reference count of this structure
+ * @sum: checksum
+ * @inverse_translations: best-effort inverse mapping
+ * @inverse_trans_unicode: best-effort inverse mapping to unicode
+ */
+struct uni_pagedict {
+ u16 **uni_pgdir[UNI_DIRS];
+ unsigned long refcount;
+ unsigned long sum;
+ unsigned char *inverse_translations[LAST_MAP + 1];
+ u16 *inverse_trans_unicode;
+};
+
+static struct uni_pagedict *dflt;
+
+static void set_inverse_transl(struct vc_data *conp, struct uni_pagedict *dict,
+ enum translation_map m)
+{
+ unsigned short *t = translations[m];
+ unsigned char *inv;
+
+ if (!dict)
+ return;
+ inv = dict->inverse_translations[m];
+
+ if (!inv) {
+ inv = dict->inverse_translations[m] = kmalloc(MAX_GLYPH,
+ GFP_KERNEL);
+ if (!inv)
+ return;
+ }
+ memset(inv, 0, MAX_GLYPH);
+
+ for (unsigned int ch = 0; ch < ARRAY_SIZE(translations[m]); ch++) {
+ int glyph = conv_uni_to_pc(conp, t[ch]);
+ if (glyph >= 0 && glyph < MAX_GLYPH && inv[glyph] < 32) {
+ /* prefer '-' above SHY etc. */
+ inv[glyph] = ch;
+ }
+ }
+}
+
+static void set_inverse_trans_unicode(struct uni_pagedict *dict)
+{
+ unsigned int d, r, g;
+ u16 *inv;
+
+ if (!dict)
+ return;
+
+ inv = dict->inverse_trans_unicode;
+ if (!inv) {
+ inv = dict->inverse_trans_unicode = kmalloc_array(MAX_GLYPH,
+ sizeof(*inv), GFP_KERNEL);
+ if (!inv)
+ return;
+ }
+ memset(inv, 0, MAX_GLYPH * sizeof(*inv));
+
+ for (d = 0; d < UNI_DIRS; d++) {
+ u16 **dir = dict->uni_pgdir[d];
+ if (!dir)
+ continue;
+ for (r = 0; r < UNI_DIR_ROWS; r++) {
+ u16 *row = dir[r];
+ if (!row)
+ continue;
+ for (g = 0; g < UNI_ROW_GLYPHS; g++) {
+ u16 glyph = row[g];
+ if (glyph < MAX_GLYPH && inv[glyph] < 32)
+ inv[glyph] = UNI(d, r, g);
+ }
+ }
+ }
+}
+
+unsigned short *set_translate(enum translation_map 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(const struct vc_data *conp, u16 glyph, bool use_unicode)
+{
+ struct uni_pagedict *p;
+ enum translation_map m;
+
+ if (glyph >= MAX_GLYPH)
+ return 0;
+
+ p = *conp->uni_pagedict_loc;
+ if (!p)
+ return glyph;
+
+ if (use_unicode) {
+ if (!p->inverse_trans_unicode)
+ return glyph;
+
+ return p->inverse_trans_unicode[glyph];
+ }
+
+ m = inv_translate[conp->vc_num];
+ if (!p->inverse_translations[m])
+ return glyph;
+
+ return p->inverse_translations[m][glyph];
+}
+EXPORT_SYMBOL_GPL(inverse_translate);
+
+static void update_user_maps(void)
+{
+ int i;
+ struct uni_pagedict *p, *q = NULL;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!vc_cons_allocated(i))
+ continue;
+ p = *vc_cons[i].d->uni_pagedict_loc;
+ if (p && p != q) {
+ set_inverse_transl(vc_cons[i].d, p, USER_MAP);
+ set_inverse_trans_unicode(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)
+{
+ unsigned short inbuf[E_TABSZ];
+ unsigned int i;
+ unsigned char ch;
+
+ for (i = 0; i < ARRAY_SIZE(inbuf); i++) {
+ if (get_user(ch, &arg[i]))
+ return -EFAULT;
+ inbuf[i] = UNI_DIRECT_BASE | ch;
+ }
+
+ guard(console_lock)();
+ memcpy(translations[USER_MAP], inbuf, sizeof(inbuf));
+ update_user_maps();
+
+ return 0;
+}
+
+int con_get_trans_old(unsigned char __user * arg)
+{
+ int i, ch;
+ unsigned short *p = translations[USER_MAP];
+ unsigned char outbuf[E_TABSZ];
+
+ scoped_guard(console_lock)
+ for (i = 0; i < ARRAY_SIZE(outbuf); i++) {
+ ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
+ outbuf[i] = (ch & ~0xff) ? 0 : ch;
+ }
+
+ return copy_to_user(arg, outbuf, sizeof(outbuf)) ? -EFAULT : 0;
+}
+
+int con_set_trans_new(ushort __user * arg)
+{
+ unsigned short inbuf[E_TABSZ];
+
+ if (copy_from_user(inbuf, arg, sizeof(inbuf)))
+ return -EFAULT;
+
+ guard(console_lock)();
+ memcpy(translations[USER_MAP], inbuf, sizeof(inbuf));
+ update_user_maps();
+
+ return 0;
+}
+
+int con_get_trans_new(ushort __user * arg)
+{
+ unsigned short outbuf[E_TABSZ];
+
+ scoped_guard(console_lock)
+ memcpy(outbuf, translations[USER_MAP], sizeof(outbuf));
+
+ return copy_to_user(arg, outbuf, sizeof(outbuf)) ? -EFAULT : 0;
+}
+
+/*
+ * Unicode -> current font conversion
+ *
+ * A font has at most 512 chars, usually 256.
+ * But one font position may represent several Unicode chars.
+ * A hashtable is somewhat of a pain to deal with, so use a
+ * "paged table" instead. Simulation has shown the memory cost of
+ * this 3-level paged table scheme to be comparable to a hash table.
+ */
+
+extern u8 dfont_unicount[]; /* Defined in console_defmap.c */
+extern u16 dfont_unitable[];
+
+static void con_release_unimap(struct uni_pagedict *dict)
+{
+ unsigned int d, r;
+
+ if (dict == dflt)
+ dflt = NULL;
+
+ for (d = 0; d < UNI_DIRS; d++) {
+ u16 **dir = dict->uni_pgdir[d];
+ if (dir != NULL) {
+ for (r = 0; r < UNI_DIR_ROWS; r++)
+ kfree(dir[r]);
+ kfree(dir);
+ }
+ dict->uni_pgdir[d] = NULL;
+ }
+
+ for (r = 0; r < ARRAY_SIZE(dict->inverse_translations); r++) {
+ kfree(dict->inverse_translations[r]);
+ dict->inverse_translations[r] = NULL;
+ }
+
+ kfree(dict->inverse_trans_unicode);
+ dict->inverse_trans_unicode = NULL;
+}
+
+/* Caller must hold the console lock */
+void con_free_unimap(struct vc_data *vc)
+{
+ struct uni_pagedict *p;
+
+ p = *vc->uni_pagedict_loc;
+ if (!p)
+ return;
+ *vc->uni_pagedict_loc = NULL;
+ if (--p->refcount)
+ return;
+ con_release_unimap(p);
+ kfree(p);
+}
+
+static int con_unify_unimap(struct vc_data *conp, struct uni_pagedict *dict1)
+{
+ struct uni_pagedict *dict2;
+ unsigned int cons, d, r;
+
+ for (cons = 0; cons < MAX_NR_CONSOLES; cons++) {
+ if (!vc_cons_allocated(cons))
+ continue;
+ dict2 = *vc_cons[cons].d->uni_pagedict_loc;
+ if (!dict2 || dict2 == dict1 || dict2->sum != dict1->sum)
+ continue;
+ for (d = 0; d < UNI_DIRS; d++) {
+ u16 **dir1 = dict1->uni_pgdir[d];
+ u16 **dir2 = dict2->uni_pgdir[d];
+ if (!dir1 && !dir2)
+ continue;
+ if (!dir1 || !dir2)
+ break;
+ for (r = 0; r < UNI_DIR_ROWS; r++) {
+ if (!dir1[r] && !dir2[r])
+ continue;
+ if (!dir1[r] || !dir2[r])
+ break;
+ if (memcmp(dir1[r], dir2[r], UNI_ROW_GLYPHS *
+ sizeof(*dir1[r])))
+ break;
+ }
+ if (r < UNI_DIR_ROWS)
+ break;
+ }
+ if (d == UNI_DIRS) {
+ dict2->refcount++;
+ *conp->uni_pagedict_loc = dict2;
+ con_release_unimap(dict1);
+ kfree(dict1);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+con_insert_unipair(struct uni_pagedict *p, u_short unicode, u_short fontpos)
+{
+ u16 **dir, *row;
+ unsigned int n;
+
+ n = UNI_DIR(unicode);
+ dir = p->uni_pgdir[n];
+ if (!dir) {
+ dir = p->uni_pgdir[n] = kcalloc(UNI_DIR_ROWS, sizeof(*dir),
+ GFP_KERNEL);
+ if (!dir)
+ return -ENOMEM;
+ }
+
+ n = UNI_ROW(unicode);
+ row = dir[n];
+ if (!row) {
+ row = dir[n] = kmalloc_array(UNI_ROW_GLYPHS, sizeof(*row),
+ GFP_KERNEL);
+ if (!row)
+ return -ENOMEM;
+ /* No glyphs for the characters (yet) */
+ memset(row, 0xff, UNI_ROW_GLYPHS * sizeof(*row));
+ }
+
+ row[UNI_GLYPH(unicode)] = fontpos;
+
+ p->sum += (fontpos << 20U) + unicode;
+
+ return 0;
+}
+
+static int con_allocate_new(struct vc_data *vc)
+{
+ struct uni_pagedict *new, *old = *vc->uni_pagedict_loc;
+
+ new = kzalloc_obj(*new);
+ if (!new)
+ return -ENOMEM;
+
+ new->refcount = 1;
+ *vc->uni_pagedict_loc = new;
+
+ if (old)
+ old->refcount--;
+
+ return 0;
+}
+
+/* Caller must hold the lock */
+static int con_do_clear_unimap(struct vc_data *vc)
+{
+ struct uni_pagedict *old = *vc->uni_pagedict_loc;
+
+ if (!old || old->refcount > 1)
+ return con_allocate_new(vc);
+
+ old->sum = 0;
+ con_release_unimap(old);
+
+ return 0;
+}
+
+int con_clear_unimap(struct vc_data *vc)
+{
+ guard(console_lock)();
+ return con_do_clear_unimap(vc);
+}
+
+static struct uni_pagedict *con_unshare_unimap(struct vc_data *vc,
+ struct uni_pagedict *old)
+{
+ struct uni_pagedict *new;
+ unsigned int d, r, g;
+ int ret;
+ u16 uni = 0;
+
+ ret = con_allocate_new(vc);
+ if (ret)
+ return ERR_PTR(ret);
+
+ new = *vc->uni_pagedict_loc;
+
+ /*
+ * uni_pgdir is a 32*32*64 table with rows allocated when its first
+ * entry is added. The unicode value must still be incremented for
+ * empty rows. We are copying entries from "old" to "new".
+ */
+ for (d = 0; d < UNI_DIRS; d++) {
+ u16 **dir = old->uni_pgdir[d];
+ if (!dir) {
+ /* Account for empty table */
+ uni += UNI_DIR_ROWS * UNI_ROW_GLYPHS;
+ continue;
+ }
+
+ for (r = 0; r < UNI_DIR_ROWS; r++) {
+ u16 *row = dir[r];
+ if (!row) {
+ /* Account for row of 64 empty entries */
+ uni += UNI_ROW_GLYPHS;
+ continue;
+ }
+
+ for (g = 0; g < UNI_ROW_GLYPHS; g++, uni++) {
+ if (row[g] == 0xffff)
+ continue;
+ /*
+ * Found one, copy entry for unicode uni with
+ * fontpos value row[g].
+ */
+ ret = con_insert_unipair(new, uni, row[g]);
+ if (ret) {
+ old->refcount++;
+ *vc->uni_pagedict_loc = old;
+ con_release_unimap(new);
+ kfree(new);
+ return ERR_PTR(ret);
+ }
+ }
+ }
+ }
+
+ return new;
+}
+
+int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
+{
+ struct uni_pagedict *dict;
+ struct unipair *plist;
+ int err = 0;
+
+ if (!ct)
+ return 0;
+
+ struct unipair *unilist __free(kvfree) = vmemdup_array_user(list, ct, sizeof(*unilist));
+ if (IS_ERR(unilist))
+ return PTR_ERR(unilist);
+
+ guard(console_lock)();
+
+ /* Save original vc_unipagdir_loc in case we allocate a new one */
+ dict = *vc->uni_pagedict_loc;
+ if (!dict)
+ return -EINVAL;
+
+ if (dict->refcount > 1) {
+ dict = con_unshare_unimap(vc, dict);
+ if (IS_ERR(dict))
+ return PTR_ERR(dict);
+ } else if (dict == dflt) {
+ dflt = NULL;
+ }
+
+ /*
+ * Insert user specified unicode pairs into new table.
+ */
+ for (plist = unilist; ct; ct--, plist++) {
+ int err1 = con_insert_unipair(dict, plist->unicode, plist->fontpos);
+ if (err1)
+ err = err1;
+ }
+
+ /*
+ * Merge with fontmaps of any other virtual consoles.
+ */
+ if (con_unify_unimap(vc, dict))
+ return err;
+
+ for (enum translation_map m = FIRST_MAP; m <= LAST_MAP; m++)
+ set_inverse_transl(vc, dict, m);
+ set_inverse_trans_unicode(dict);
+
+ return err;
+}
+
+/**
+ * con_set_default_unimap - set default unicode map
+ * @vc: the console we are updating
+ *
+ * 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 video setup, and when the
+ * PIO_FONTRESET ioctl is called.
+ *
+ * The caller must hold the console lock
+ */
+int con_set_default_unimap(struct vc_data *vc)
+{
+ struct uni_pagedict *dict;
+ unsigned int fontpos, count;
+ int err = 0, err1;
+ u16 *dfont;
+
+ if (dflt) {
+ dict = *vc->uni_pagedict_loc;
+ if (dict == dflt)
+ return 0;
+
+ dflt->refcount++;
+ *vc->uni_pagedict_loc = dflt;
+ if (dict && !--dict->refcount) {
+ con_release_unimap(dict);
+ kfree(dict);
+ }
+ return 0;
+ }
+
+ /* The default font is always 256 characters */
+
+ err = con_do_clear_unimap(vc);
+ if (err)
+ return err;
+
+ dict = *vc->uni_pagedict_loc;
+ dfont = dfont_unitable;
+
+ for (fontpos = 0; fontpos < 256U; fontpos++)
+ for (count = dfont_unicount[fontpos]; count; count--) {
+ err1 = con_insert_unipair(dict, *(dfont++), fontpos);
+ if (err1)
+ err = err1;
+ }
+
+ if (con_unify_unimap(vc, dict)) {
+ dflt = *vc->uni_pagedict_loc;
+ return err;
+ }
+
+ for (enum translation_map m = FIRST_MAP; m <= LAST_MAP; m++)
+ set_inverse_transl(vc, dict, m);
+ set_inverse_trans_unicode(dict);
+ dflt = dict;
+ return err;
+}
+EXPORT_SYMBOL(con_set_default_unimap);
+
+/**
+ * con_copy_unimap - copy unimap between two vts
+ * @dst_vc: target
+ * @src_vc: source
+ *
+ * The caller must hold the console lock when invoking this method
+ */
+int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
+{
+ struct uni_pagedict *src;
+
+ if (!*src_vc->uni_pagedict_loc)
+ return -EINVAL;
+ if (*dst_vc->uni_pagedict_loc == *src_vc->uni_pagedict_loc)
+ return 0;
+ con_free_unimap(dst_vc);
+ src = *src_vc->uni_pagedict_loc;
+ src->refcount++;
+ *dst_vc->uni_pagedict_loc = src;
+ return 0;
+}
+EXPORT_SYMBOL(con_copy_unimap);
+
+/*
+ * con_get_unimap - get the unicode map
+ *
+ * Read the console unicode data for this console. Called from the ioctl
+ * handlers.
+ */
+int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct,
+ struct unipair __user *list)
+{
+ ushort ect;
+ struct uni_pagedict *dict;
+ unsigned int d, r, g;
+
+ struct unipair *unilist __free(kvfree) = kvmalloc_objs(*unilist, ct,
+ GFP_KERNEL);
+ if (!unilist)
+ return -ENOMEM;
+
+ scoped_guard(console_lock) {
+ ect = 0;
+ dict = *vc->uni_pagedict_loc;
+ if (!dict)
+ break;
+
+ for (d = 0; d < UNI_DIRS; d++) {
+ u16 **dir = dict->uni_pgdir[d];
+ if (!dir)
+ continue;
+
+ for (r = 0; r < UNI_DIR_ROWS; r++) {
+ u16 *row = dir[r];
+ if (!row)
+ continue;
+
+ for (g = 0; g < UNI_ROW_GLYPHS; g++, row++) {
+ if (*row >= MAX_GLYPH)
+ continue;
+ if (ect < ct) {
+ unilist[ect].unicode = UNI(d, r, g);
+ unilist[ect].fontpos = *row;
+ }
+ ect++;
+ }
+ }
+ }
+ }
+
+ if (copy_to_user(list, unilist, min(ect, ct) * sizeof(*unilist)))
+ return -EFAULT;
+ if (put_user(ect, uct))
+ return -EFAULT;
+ if (ect > ct)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * 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.
+ *
+ * FIXME: at some point we need to decide if we want to lock the table
+ * update element itself via the keyboard_event_lock for consistency with the
+ * keyboard driver as well as the consoles
+ */
+/* 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 < ARRAY_SIZE(translations[USER_MAP]); 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)
+{
+ struct uni_pagedict *dict;
+ u16 **dir, *row, glyph;
+
+ /* 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 */
+ /*
+ * 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;
+
+ dict = *conp->uni_pagedict_loc;
+ if (!dict)
+ return -3;
+
+ dir = dict->uni_pgdir[UNI_DIR(ucs)];
+ if (!dir)
+ return -4;
+
+ row = dir[UNI_ROW(ucs)];
+ if (!row)
+ return -4;
+
+ glyph = row[UNI_GLYPH(ucs)];
+ if (glyph >= MAX_GLYPH)
+ return -4;
+
+ return glyph;
+}
+
+/*
+ * 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->uni_pagedict_loc)
+ con_set_default_unimap(vc_cons[i].d);
+}
+
diff --git a/drivers/tty/vt/cp437.uni b/drivers/tty/vt/cp437.uni
new file mode 100644
index 000000000..a1991904c
--- /dev/null
+++ b/drivers/tty/vt/cp437.uni
@@ -0,0 +1,292 @@
+# SPDX-License-Identifier: GPL-2.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/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped
new file mode 100644
index 000000000..6af7bf8d5
--- /dev/null
+++ b/drivers/tty/vt/defkeymap.c_shipped
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Do not edit this file! It was automatically generated by */
+/* loadkeys --mktable --unicode defkeymap.map > defkeymap.c */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static unsigned 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,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+unsigned short *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', 0x00c0}, {'`', 'a', 0x00e0},
+ {'\'', 'A', 0x00c1}, {'\'', 'a', 0x00e1},
+ {'^', 'A', 0x00c2}, {'^', 'a', 0x00e2},
+ {'~', 'A', 0x00c3}, {'~', 'a', 0x00e3},
+ {'"', 'A', 0x00c4}, {'"', 'a', 0x00e4},
+ {'O', 'A', 0x00c5}, {'o', 'a', 0x00e5},
+ {'0', 'A', 0x00c5}, {'0', 'a', 0x00e5},
+ {'A', 'A', 0x00c5}, {'a', 'a', 0x00e5},
+ {'A', 'E', 0x00c6}, {'a', 'e', 0x00e6},
+ {',', 'C', 0x00c7}, {',', 'c', 0x00e7},
+ {'`', 'E', 0x00c8}, {'`', 'e', 0x00e8},
+ {'\'', 'E', 0x00c9}, {'\'', 'e', 0x00e9},
+ {'^', 'E', 0x00ca}, {'^', 'e', 0x00ea},
+ {'"', 'E', 0x00cb}, {'"', 'e', 0x00eb},
+ {'`', 'I', 0x00cc}, {'`', 'i', 0x00ec},
+ {'\'', 'I', 0x00cd}, {'\'', 'i', 0x00ed},
+ {'^', 'I', 0x00ce}, {'^', 'i', 0x00ee},
+ {'"', 'I', 0x00cf}, {'"', 'i', 0x00ef},
+ {'-', 'D', 0x00d0}, {'-', 'd', 0x00f0},
+ {'~', 'N', 0x00d1}, {'~', 'n', 0x00f1},
+ {'`', 'O', 0x00d2}, {'`', 'o', 0x00f2},
+ {'\'', 'O', 0x00d3}, {'\'', 'o', 0x00f3},
+ {'^', 'O', 0x00d4}, {'^', 'o', 0x00f4},
+ {'~', 'O', 0x00d5}, {'~', 'o', 0x00f5},
+ {'"', 'O', 0x00d6}, {'"', 'o', 0x00f6},
+ {'/', 'O', 0x00d8}, {'/', 'o', 0x00f8},
+ {'`', 'U', 0x00d9}, {'`', 'u', 0x00f9},
+ {'\'', 'U', 0x00da}, {'\'', 'u', 0x00fa},
+ {'^', 'U', 0x00db}, {'^', 'u', 0x00fb},
+ {'"', 'U', 0x00dc}, {'"', 'u', 0x00fc},
+ {'\'', 'Y', 0x00dd}, {'\'', 'y', 0x00fd},
+ {'T', 'H', 0x00de}, {'t', 'h', 0x00fe},
+ {'s', 's', 0x00df}, {'"', 'y', 0x00ff},
+ {'s', 'z', 0x00df}, {'i', 'j', 0x00ff},
+};
+
+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 000000000..37f1ac6dd
--- /dev/null
+++ b/drivers/tty/vt/defkeymap.map
@@ -0,0 +1,358 @@
+# SPDX-License-Identifier: GPL-2.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/tty/vt/gen_ucs_fallback_table.py b/drivers/tty/vt/gen_ucs_fallback_table.py
new file mode 100755
index 000000000..6e09c1cb6
--- /dev/null
+++ b/drivers/tty/vt/gen_ucs_fallback_table.py
@@ -0,0 +1,360 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Leverage Python's unidecode module to generate ucs_fallback_table.h
+#
+# The generated table maps complex characters to their simpler fallback forms
+# for a terminal display when corresponding glyphs are unavailable.
+#
+# Usage:
+# python3 gen_ucs_fallback_table.py # Generate fallback tables
+# python3 gen_ucs_fallback_table.py -o FILE # Specify output file
+
+import unicodedata
+from unidecode import unidecode
+import sys
+import argparse
+from collections import defaultdict
+
+# Try to get unidecode version
+try:
+ from importlib.metadata import version
+ unidecode_version = version('unidecode')
+except:
+ unidecode_version = 'unknown'
+
+# This script's file name
+from pathlib import Path
+this_file = Path(__file__).name
+
+# Default output file name
+DEFAULT_OUT_FILE = "ucs_fallback_table.h"
+
+# Define the range marker value
+RANGE_MARKER = 0x00
+
+def generate_fallback_map():
+ """Generate a fallback map using unidecode for all relevant Unicode points."""
+ fallback_map = {}
+
+ # Process BMP characters (0x0000 - 0xFFFF) to keep table size manageable
+ for cp in range(0x0080, 0x10000): # Skip ASCII range (0x00-0x7F)
+ char = chr(cp)
+
+ # Skip unassigned/control characters
+ try:
+ if not unicodedata.name(char, ''):
+ continue
+ except ValueError:
+ continue
+
+ # Get the unidecode transliteration
+ ascii_version = unidecode(char)
+
+ # Only store if it results in a single character mapping
+ if len(ascii_version) == 1:
+ fallback_map[cp] = ord(ascii_version)
+
+ # Apply manual overrides for special cases
+ fallback_map.update(get_special_overrides())
+
+ return fallback_map
+
+def get_special_overrides():
+ """Get special case overrides that need different handling than unidecode
+ provides... or doesn't provide at all."""
+
+ overrides = {}
+
+ # Multi-character unidecode output
+ # These map to single chars instead of unidecode's multiple-char mappings
+ # In a terminal fallback context, we need a single character rather than multiple
+ overrides[0x00C6] = ord('E') # Æ LATIN CAPITAL LETTER AE -> E (unidecode: "AE")
+ overrides[0x00E6] = ord('e') # æ LATIN SMALL LETTER AE -> e (unidecode: "ae")
+ overrides[0x0152] = ord('E') # Å’ LATIN CAPITAL LIGATURE OE -> E (unidecode: "OE")
+ overrides[0x0153] = ord('e') # Å“ LATIN SMALL LETTER LIGATURE OE -> e (unidecode: "oe")
+ overrides[0x00DF] = ord('s') # ß LATIN SMALL LETTER SHARP S -> s (unidecode: "ss")
+
+ # Comparison operators that unidecode renders as multiple characters
+ overrides[0x2264] = ord('<') # ≤ LESS-THAN OR EQUAL TO -> < (unidecode: "<=")
+ overrides[0x2265] = ord('>') # ≥ GREATER-THAN OR EQUAL TO -> > (unidecode: ">=")
+
+ # Unidecode returns an empty string for these
+ overrides[0x2260] = ord('#') # ≠ NOT EQUAL TO -> # (unidecode: empty string)
+
+ # Quadrant block characters that unidecode doesn't map
+ for cp in range(0x2596, 0x259F+1):
+ overrides[cp] = ord('#') # â–– â–— â–˜ â–™ etc. - map to # (unidecode: empty string)
+
+ # Directional arrows
+ # These provide better semantic meaning than unidecode's mappings
+ overrides[0x2192] = ord('>') # → RIGHTWARDS ARROW -> > (unidecode: "-")
+ overrides[0x2190] = ord('<') # � LEFTWARDS ARROW -> < (unidecode: "-")
+ overrides[0x2191] = ord('^') # ↑ UPWARDS ARROW -> ^ (unidecode: "|")
+ overrides[0x2193] = ord('v') # ↓ DOWNWARDS ARROW -> v (unidecode: "|")
+
+ # Double arrows with their directional semantic mappings
+ overrides[0x21D0] = ord('<') # � LEFTWARDS DOUBLE ARROW -> <
+ overrides[0x21D1] = ord('^') # ⇑ UPWARDS DOUBLE ARROW -> ^
+ overrides[0x21D2] = ord('>') # ⇒ RIGHTWARDS DOUBLE ARROW -> >
+ overrides[0x21D3] = ord('v') # ⇓ DOWNWARDS DOUBLE ARROW -> v
+
+ # Halfwidth arrows
+ # These need the same treatment as their normal-width counterparts
+ overrides[0xFFE9] = ord('<') # ï¿© HALFWIDTH LEFTWARDS ARROW -> < (unidecode: "-")
+ overrides[0xFFEA] = ord('^') # ↑ HALFWIDTH UPWARDS ARROW -> ^ (unidecode: "|")
+ overrides[0xFFEB] = ord('>') # ï¿« HALFWIDTH RIGHTWARDS ARROW -> > (unidecode: "-")
+ overrides[0xFFEC] = ord('v') # ↓ HALFWIDTH DOWNWARDS ARROW -> v (unidecode: "|")
+
+ # Currency symbols - each mapped to a representative letter
+ overrides[0x00A2] = ord('c') # ¢ CENT SIGN -> c
+ overrides[0x00A3] = ord('L') # £ POUND SIGN -> L
+ overrides[0x00A5] = ord('Y') # ¥ YEN SIGN -> Y
+ overrides[0x20AC] = ord('E') # € EURO SIGN -> E
+
+ # Symbols mapped to letters
+ overrides[0x00A7] = ord('S') # § SECTION SIGN -> S
+ overrides[0x00A9] = ord('C') # © COPYRIGHT SIGN -> C
+ overrides[0x00AE] = ord('R') # ® REGISTERED SIGN -> R
+ overrides[0x2122] = ord('T') # â„¢ TRADE MARK SIGN -> T
+
+ # Degree-related symbols
+ overrides[0x00B0] = ord('o') # ° DEGREE SIGN -> o
+ overrides[0x2103] = ord('C') # ℃ DEGREE CELSIUS -> C
+ overrides[0x2109] = ord('F') # ℉ DEGREE FAHRENHEIT -> F
+
+ # Angle quotation marks
+ overrides[0x00AB] = ord('<') # « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -> <
+ overrides[0x00BB] = ord('>') # » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -> >
+
+ # Operators with circular shape
+ overrides[0x2218] = ord('o') # ∘ RING OPERATOR -> o
+ overrides[0x2219] = ord('.') # ∙ BULLET OPERATOR -> .
+
+ # Negated mathematical symbols (preserving the negation semantics)
+ # Negated symbols mapped to exclamation mark (semantically "not")
+ for cp in (0x2204, 0x2209, 0x220C, 0x2224, 0x2226, 0x226E, 0x226F, 0x2280, 0x2281, 0x2284, 0x2285):
+ overrides[cp] = ord('!') # Negated math symbols -> ! (not)
+
+ # Negated symbols mapped to hash sign (semantically "not equal")
+ for cp in (0x2241, 0x2244, 0x2249, 0x2262, 0x2268, 0x2269, 0x226D, 0x228A, 0x228B):
+ overrides[cp] = ord('#') # Negated equality symbols -> # (not equal)
+
+ # Negated arrows - all mapped to exclamation mark
+ for cp in (0x219A, 0x219B, 0x21AE, 0x21CD, 0x21CE, 0x21CF):
+ overrides[cp] = ord('!') # Negated arrows -> ! (not)
+
+ # Dashes and hyphens
+ for cp in (0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2043, 0x2052):
+ overrides[cp] = ord('-') # Dashes and hyphens -> -
+
+ # Question mark punctuation
+ for cp in (0x203D, 0x2047, 0x2048):
+ overrides[cp] = ord('?') # Question marks -> ?
+
+ # Exclamation mark punctuation
+ for cp in (0x203C, 0x2049):
+ overrides[cp] = ord('!') # Exclamation marks -> !
+
+ # Asterisk-like symbols
+ for cp in (0x2042, 0x2051, 0x2055):
+ overrides[cp] = ord('*')
+
+ # Other specific punctuation with unique mappings
+ overrides[0x201E] = ord('"') # „ DOUBLE LOW-9 QUOTATION MARK
+ overrides[0x2023] = ord('>') # ‣ TRIANGULAR BULLET
+ overrides[0x2026] = ord('.') # … HORIZONTAL ELLIPSIS
+ overrides[0x2033] = ord('"') # ″ DOUBLE PRIME
+ overrides[0x204B] = ord('P') # � REVERSED PILCROW SIGN
+ overrides[0x204C] = ord('<') # � BLACK LEFTWARDS BULLET
+ overrides[0x204D] = ord('>') # � BLACK RIGHTWARDS BULLET
+ overrides[0x204F] = ord(';') # � REVERSED SEMICOLON
+ overrides[0x205B] = ord(':') # � FOUR DOT MARK
+
+ # Check marks
+ overrides[0x2713] = ord('v') # ✓ CHECK MARK
+ overrides[0x2714] = ord('V') # ✔ HEAVY CHECK MARK
+
+ # X marks - lowercase for regular, uppercase for heavy
+ for cp in (0x2715, 0x2717):
+ overrides[cp] = ord('x') # Regular X marks -> x
+ for cp in (0x2716, 0x2718):
+ overrides[cp] = ord('X') # Heavy X marks -> X
+
+ # Stars and asterisk-like symbols mapped to '*'
+ for cp in (0x2605, 0x2606, 0x262A, 0x269D, 0x2698):
+ overrides[cp] = ord('*') # All star and asterisk symbols -> *
+ for cp in range(0x2721, 0x2746+1):
+ overrides[cp] = ord('*') # All star and asterisk symbols -> *
+ for cp in range(0x2749, 0x274B+1):
+ overrides[cp] = ord('*') # Last set of asterisk symbols -> *
+ for cp in (0x229B, 0x22C6, 0x235F, 0x2363):
+ overrides[cp] = ord('*') # Star operators -> *
+
+ # Special exclusions with fallback value of 0
+ # These will be filtered out in organize_by_pages()
+
+ # Exclude U+2028 (LINE SEPARATOR)
+ overrides[0x2028] = 0 # LINE SEPARATOR (unidecode: '\n')
+
+ # Full-width to ASCII mapping (covering all printable ASCII 33-126)
+ # 0xFF01 (�) to 0xFF5E (~) -> ASCII 33 (!) to 126 (~)
+ # Those are excluded here to reduce the table size.
+ # It is more efficient to process them programmatically in
+ # ucs.c:ucs_get_fallback().
+ for cp in range(0xFF01, 0xFF5E + 1):
+ overrides[cp] = 0 # Double-width ASCII characters
+
+ return overrides
+
+def organize_by_pages(fallback_map):
+ """Organize the fallback mappings by their high byte (page)."""
+ # Group by high byte (page)
+ page_groups = defaultdict(list)
+ for code, fallback in fallback_map.items():
+ # Skip characters with fallback value of 0 (excluded characters)
+ if fallback == 0:
+ continue
+
+ page = code >> 8 # Get the high byte (page)
+ offset = code & 0xFF # Get the low byte (offset within page)
+ page_groups[page].append((offset, fallback))
+
+ # Sort each page's entries by offset
+ for page in page_groups:
+ page_groups[page].sort()
+
+ return page_groups
+
+def compress_ranges(page_groups):
+ """Compress consecutive entries with the same fallback character into ranges.
+ A range is only compressed if it contains 3 or more consecutive entries."""
+
+ compressed_pages = {}
+
+ for page, entries in page_groups.items():
+ compressed_entries = []
+ i = 0
+ while i < len(entries):
+ start_offset, fallback = entries[i]
+
+ # Look ahead to find consecutive entries with the same fallback
+ j = i + 1
+ while (j < len(entries) and
+ entries[j][0] == entries[j-1][0] + 1 and # consecutive offsets
+ entries[j][1] == fallback): # same fallback
+ j += 1
+
+ # Calculate the range end
+ end_offset = entries[j-1][0]
+
+ # If we found a range with 3 or more entries (worth compressing)
+ if j - i >= 3:
+ # Add a range entry
+ compressed_entries.append((start_offset, RANGE_MARKER))
+ compressed_entries.append((end_offset, fallback))
+ else:
+ # Add the individual entries as is
+ for k in range(i, j):
+ compressed_entries.append(entries[k])
+
+ i = j
+
+ compressed_pages[page] = compressed_entries
+
+ return compressed_pages
+
+def cp_name(cp):
+ """Get the Unicode character name for a code point."""
+ try:
+ return unicodedata.name(chr(cp))
+ except:
+ return f"U+{cp:04X}"
+
+def generate_fallback_tables(out_file=DEFAULT_OUT_FILE):
+ """Generate the fallback character tables."""
+ # Generate fallback map using unidecode
+ fallback_map = generate_fallback_map()
+ print(f"Generated {len(fallback_map)} total fallback mappings")
+
+ # Organize by pages
+ page_groups = organize_by_pages(fallback_map)
+ print(f"Organized into {len(page_groups)} pages")
+
+ # Compress ranges
+ compressed_pages = compress_ranges(page_groups)
+ total_compressed_entries = sum(len(entries) for entries in compressed_pages.values())
+ print(f"Total compressed entries: {total_compressed_entries}")
+
+ # Create output file
+ with open(out_file, 'w') as f:
+ f.write(f"""\
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * {out_file} - Unicode character fallback table
+ *
+ * Auto-generated by {this_file}
+ *
+ * Unicode Version: {unicodedata.unidata_version}
+ * Unidecode Version: {unidecode_version}
+ *
+ * This file contains optimized tables that map complex Unicode characters
+ * to simpler fallback characters for terminal display when corresponding
+ * glyphs are unavailable.
+ */
+
+static const struct ucs_page_desc ucs_fallback_pages[] = {{
+""")
+
+ # Convert compressed_pages to a sorted list of (page, entries) tuples
+ sorted_pages = sorted(compressed_pages.items())
+
+ # Track the start index for each page
+ start_index = 0
+
+ # Write page descriptors
+ for page, entries in sorted_pages:
+ count = len(entries)
+ f.write(f"\t{{ 0x{page:02X}, {count}, {start_index} }},\n")
+ start_index += count
+
+ # Write entries array
+ f.write("""\
+};
+
+/* Page entries array (referenced by page descriptors) */
+static const struct ucs_page_entry ucs_fallback_entries[] = {
+""")
+
+ # Write all entries
+ for page, entries in sorted_pages:
+ page_hex = f"0x{page:02X}"
+ f.write(f"\t/* Entries for page {page_hex} */\n")
+
+ for i, (offset, fallback) in enumerate(entries):
+ # Convert to hex for better readability
+ offset_hex = f"0x{offset:02X}"
+ fallback_hex = f"0x{fallback:02X}"
+
+ # Handle comments
+ codepoint = (page << 8) | offset
+
+ if fallback == RANGE_MARKER:
+ comment = f"{cp_name(codepoint)} -> ..."
+ else:
+ comment = f"{cp_name(codepoint)} -> '{chr(fallback)}'"
+ f.write(f"\t{{ 0x{offset:02X}, 0x{fallback:02X} }}, /* {comment} */\n")
+
+ f.write(f"""\
+}};
+
+#define UCS_PAGE_ENTRY_RANGE_MARKER {RANGE_MARKER}
+""")
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Generate Unicode fallback character tables")
+ parser.add_argument("-o", "--output", dest="output_file", default=DEFAULT_OUT_FILE,
+ help=f"Output file name (default: {DEFAULT_OUT_FILE})")
+ args = parser.parse_args()
+
+ generate_fallback_tables(out_file=args.output_file)
diff --git a/drivers/tty/vt/gen_ucs_recompose_table.py b/drivers/tty/vt/gen_ucs_recompose_table.py
new file mode 100755
index 000000000..4434a436a
--- /dev/null
+++ b/drivers/tty/vt/gen_ucs_recompose_table.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Leverage Python's unicodedata module to generate ucs_recompose_table.h
+#
+# The generated table maps base character + combining mark pairs to their
+# precomposed equivalents.
+#
+# Usage:
+# python3 gen_ucs_recompose_table.py # Generate with common recomposition pairs
+# python3 gen_ucs_recompose_table.py --full # Generate with all recomposition pairs
+
+import unicodedata
+import sys
+import argparse
+import textwrap
+
+# This script's file name
+from pathlib import Path
+this_file = Path(__file__).name
+
+# Default output file name
+DEFAULT_OUT_FILE = "ucs_recompose_table.h"
+
+common_recompose_description = "most commonly used Latin, Greek, and Cyrillic recomposition pairs only"
+COMMON_RECOMPOSITION_PAIRS = [
+ # Latin letters with accents - uppercase
+ (0x0041, 0x0300, 0x00C0), # A + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER A WITH GRAVE
+ (0x0041, 0x0301, 0x00C1), # A + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER A WITH ACUTE
+ (0x0041, 0x0302, 0x00C2), # A + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+ (0x0041, 0x0303, 0x00C3), # A + COMBINING TILDE = LATIN CAPITAL LETTER A WITH TILDE
+ (0x0041, 0x0308, 0x00C4), # A + COMBINING DIAERESIS = LATIN CAPITAL LETTER A WITH DIAERESIS
+ (0x0041, 0x030A, 0x00C5), # A + COMBINING RING ABOVE = LATIN CAPITAL LETTER A WITH RING ABOVE
+ (0x0043, 0x0327, 0x00C7), # C + COMBINING CEDILLA = LATIN CAPITAL LETTER C WITH CEDILLA
+ (0x0045, 0x0300, 0x00C8), # E + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER E WITH GRAVE
+ (0x0045, 0x0301, 0x00C9), # E + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER E WITH ACUTE
+ (0x0045, 0x0302, 0x00CA), # E + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+ (0x0045, 0x0308, 0x00CB), # E + COMBINING DIAERESIS = LATIN CAPITAL LETTER E WITH DIAERESIS
+ (0x0049, 0x0300, 0x00CC), # I + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER I WITH GRAVE
+ (0x0049, 0x0301, 0x00CD), # I + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER I WITH ACUTE
+ (0x0049, 0x0302, 0x00CE), # I + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+ (0x0049, 0x0308, 0x00CF), # I + COMBINING DIAERESIS = LATIN CAPITAL LETTER I WITH DIAERESIS
+ (0x004E, 0x0303, 0x00D1), # N + COMBINING TILDE = LATIN CAPITAL LETTER N WITH TILDE
+ (0x004F, 0x0300, 0x00D2), # O + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER O WITH GRAVE
+ (0x004F, 0x0301, 0x00D3), # O + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER O WITH ACUTE
+ (0x004F, 0x0302, 0x00D4), # O + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+ (0x004F, 0x0303, 0x00D5), # O + COMBINING TILDE = LATIN CAPITAL LETTER O WITH TILDE
+ (0x004F, 0x0308, 0x00D6), # O + COMBINING DIAERESIS = LATIN CAPITAL LETTER O WITH DIAERESIS
+ (0x0055, 0x0300, 0x00D9), # U + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER U WITH GRAVE
+ (0x0055, 0x0301, 0x00DA), # U + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER U WITH ACUTE
+ (0x0055, 0x0302, 0x00DB), # U + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+ (0x0055, 0x0308, 0x00DC), # U + COMBINING DIAERESIS = LATIN CAPITAL LETTER U WITH DIAERESIS
+ (0x0059, 0x0301, 0x00DD), # Y + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER Y WITH ACUTE
+
+ # Latin letters with accents - lowercase
+ (0x0061, 0x0300, 0x00E0), # a + COMBINING GRAVE ACCENT = LATIN SMALL LETTER A WITH GRAVE
+ (0x0061, 0x0301, 0x00E1), # a + COMBINING ACUTE ACCENT = LATIN SMALL LETTER A WITH ACUTE
+ (0x0061, 0x0302, 0x00E2), # a + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER A WITH CIRCUMFLEX
+ (0x0061, 0x0303, 0x00E3), # a + COMBINING TILDE = LATIN SMALL LETTER A WITH TILDE
+ (0x0061, 0x0308, 0x00E4), # a + COMBINING DIAERESIS = LATIN SMALL LETTER A WITH DIAERESIS
+ (0x0061, 0x030A, 0x00E5), # a + COMBINING RING ABOVE = LATIN SMALL LETTER A WITH RING ABOVE
+ (0x0063, 0x0327, 0x00E7), # c + COMBINING CEDILLA = LATIN SMALL LETTER C WITH CEDILLA
+ (0x0065, 0x0300, 0x00E8), # e + COMBINING GRAVE ACCENT = LATIN SMALL LETTER E WITH GRAVE
+ (0x0065, 0x0301, 0x00E9), # e + COMBINING ACUTE ACCENT = LATIN SMALL LETTER E WITH ACUTE
+ (0x0065, 0x0302, 0x00EA), # e + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER E WITH CIRCUMFLEX
+ (0x0065, 0x0308, 0x00EB), # e + COMBINING DIAERESIS = LATIN SMALL LETTER E WITH DIAERESIS
+ (0x0069, 0x0300, 0x00EC), # i + COMBINING GRAVE ACCENT = LATIN SMALL LETTER I WITH GRAVE
+ (0x0069, 0x0301, 0x00ED), # i + COMBINING ACUTE ACCENT = LATIN SMALL LETTER I WITH ACUTE
+ (0x0069, 0x0302, 0x00EE), # i + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER I WITH CIRCUMFLEX
+ (0x0069, 0x0308, 0x00EF), # i + COMBINING DIAERESIS = LATIN SMALL LETTER I WITH DIAERESIS
+ (0x006E, 0x0303, 0x00F1), # n + COMBINING TILDE = LATIN SMALL LETTER N WITH TILDE
+ (0x006F, 0x0300, 0x00F2), # o + COMBINING GRAVE ACCENT = LATIN SMALL LETTER O WITH GRAVE
+ (0x006F, 0x0301, 0x00F3), # o + COMBINING ACUTE ACCENT = LATIN SMALL LETTER O WITH ACUTE
+ (0x006F, 0x0302, 0x00F4), # o + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER O WITH CIRCUMFLEX
+ (0x006F, 0x0303, 0x00F5), # o + COMBINING TILDE = LATIN SMALL LETTER O WITH TILDE
+ (0x006F, 0x0308, 0x00F6), # o + COMBINING DIAERESIS = LATIN SMALL LETTER O WITH DIAERESIS
+ (0x0075, 0x0300, 0x00F9), # u + COMBINING GRAVE ACCENT = LATIN SMALL LETTER U WITH GRAVE
+ (0x0075, 0x0301, 0x00FA), # u + COMBINING ACUTE ACCENT = LATIN SMALL LETTER U WITH ACUTE
+ (0x0075, 0x0302, 0x00FB), # u + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER U WITH CIRCUMFLEX
+ (0x0075, 0x0308, 0x00FC), # u + COMBINING DIAERESIS = LATIN SMALL LETTER U WITH DIAERESIS
+ (0x0079, 0x0301, 0x00FD), # y + COMBINING ACUTE ACCENT = LATIN SMALL LETTER Y WITH ACUTE
+ (0x0079, 0x0308, 0x00FF), # y + COMBINING DIAERESIS = LATIN SMALL LETTER Y WITH DIAERESIS
+
+ # Common Greek characters
+ (0x0391, 0x0301, 0x0386), # Α + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ALPHA WITH TONOS
+ (0x0395, 0x0301, 0x0388), # Ε + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER EPSILON WITH TONOS
+ (0x0397, 0x0301, 0x0389), # Η + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ETA WITH TONOS
+ (0x0399, 0x0301, 0x038A), # Ι + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER IOTA WITH TONOS
+ (0x039F, 0x0301, 0x038C), # Ο + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMICRON WITH TONOS
+ (0x03A5, 0x0301, 0x038E), # Υ + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER UPSILON WITH TONOS
+ (0x03A9, 0x0301, 0x038F), # Ω + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMEGA WITH TONOS
+ (0x03B1, 0x0301, 0x03AC), # α + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ALPHA WITH TONOS
+ (0x03B5, 0x0301, 0x03AD), # ε + COMBINING ACUTE ACCENT = GREEK SMALL LETTER EPSILON WITH TONOS
+ (0x03B7, 0x0301, 0x03AE), # η + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ETA WITH TONOS
+ (0x03B9, 0x0301, 0x03AF), # ι + COMBINING ACUTE ACCENT = GREEK SMALL LETTER IOTA WITH TONOS
+ (0x03BF, 0x0301, 0x03CC), # ο + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMICRON WITH TONOS
+ (0x03C5, 0x0301, 0x03CD), # Ï… + COMBINING ACUTE ACCENT = GREEK SMALL LETTER UPSILON WITH TONOS
+ (0x03C9, 0x0301, 0x03CE), # ω + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMEGA WITH TONOS
+
+ # Common Cyrillic characters
+ (0x0418, 0x0306, 0x0419), # И + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT I
+ (0x0438, 0x0306, 0x0439), # и + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT I
+ (0x0423, 0x0306, 0x040E), # У + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT U
+ (0x0443, 0x0306, 0x045E), # у + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT U
+]
+
+full_recompose_description = "all possible recomposition pairs from the Unicode BMP"
+def collect_all_recomposition_pairs():
+ """Collect all possible recomposition pairs from the Unicode data."""
+ # Map to store recomposition pairs: (base, combining) -> recomposed
+ recompose_map = {}
+
+ # Process all assigned Unicode code points in BMP (Basic Multilingual Plane)
+ # We limit to BMP (0x0000-0xFFFF) to keep our table smaller with uint16_t
+ for cp in range(0, 0x10000):
+ try:
+ char = chr(cp)
+
+ # Skip unassigned or control characters
+ if not unicodedata.name(char, ''):
+ continue
+
+ # Find decomposition
+ decomp = unicodedata.decomposition(char)
+ if not decomp or '<' in decomp: # Skip compatibility decompositions
+ continue
+
+ # Parse the decomposition
+ parts = decomp.split()
+ if len(parts) == 2: # Simple base + combining mark
+ base = int(parts[0], 16)
+ combining = int(parts[1], 16)
+
+ # Only store if both are in BMP
+ if base < 0x10000 and combining < 0x10000:
+ recompose_map[(base, combining)] = cp
+
+ except (ValueError, TypeError):
+ continue
+
+ # Convert to a list of tuples and sort for binary search
+ recompose_list = [(base, combining, recomposed)
+ for (base, combining), recomposed in recompose_map.items()]
+ recompose_list.sort()
+
+ return recompose_list
+
+def validate_common_pairs(full_list):
+ """Validate that all common pairs are in the full list.
+
+ Raises:
+ ValueError: If any common pair is missing or has a different recomposition
+ value than what's in the full table.
+ """
+ full_pairs = {(base, combining): recomposed for base, combining, recomposed in full_list}
+ for base, combining, recomposed in COMMON_RECOMPOSITION_PAIRS:
+ full_recomposed = full_pairs.get((base, combining))
+ if full_recomposed is None:
+ error_msg = f"Error: Common pair (0x{base:04X}, 0x{combining:04X}) not found in full data"
+ print(error_msg)
+ raise ValueError(error_msg)
+ elif full_recomposed != recomposed:
+ error_msg = (f"Error: Common pair (0x{base:04X}, 0x{combining:04X}) has different recomposition: "
+ f"0x{recomposed:04X} vs 0x{full_recomposed:04X}")
+ print(error_msg)
+ raise ValueError(error_msg)
+
+def generate_recomposition_table(use_full_list=False, out_file=DEFAULT_OUT_FILE):
+ """Generate the recomposition C table."""
+
+ # Collect all recomposition pairs for validation
+ full_recompose_list = collect_all_recomposition_pairs()
+
+ # Decide which list to use
+ if use_full_list:
+ print("Using full recomposition list...")
+ recompose_list = full_recompose_list
+ table_description = full_recompose_description
+ alt_list = COMMON_RECOMPOSITION_PAIRS
+ alt_description = common_recompose_description
+ else:
+ print("Using common recomposition list...")
+ # Validate that all common pairs are in the full list
+ validate_common_pairs(full_recompose_list)
+ recompose_list = sorted(COMMON_RECOMPOSITION_PAIRS)
+ table_description = common_recompose_description
+ alt_list = full_recompose_list
+ alt_description = full_recompose_description
+ generation_mode = " --full" if use_full_list else ""
+ alternative_mode = " --full" if not use_full_list else ""
+ table_description_detail = f"{table_description} ({len(recompose_list)} entries)"
+ alt_description_detail = f"{alt_description} ({len(alt_list)} entries)"
+
+ # Calculate min/max values for boundary checks
+ min_base = min(base for base, _, _ in recompose_list)
+ max_base = max(base for base, _, _ in recompose_list)
+ min_combining = min(combining for _, combining, _ in recompose_list)
+ max_combining = max(combining for _, combining, _ in recompose_list)
+
+ # Generate implementation file
+ with open(out_file, 'w') as f:
+ f.write(f"""\
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * {out_file} - Unicode character recomposition
+ *
+ * Auto-generated by {this_file}{generation_mode}
+ *
+ * Unicode Version: {unicodedata.unidata_version}
+ *
+{textwrap.fill(
+ f"This file contains a table with {table_description_detail}. " +
+ f"To generate a table with {alt_description_detail} instead, run:",
+ width=75, initial_indent=" * ", subsequent_indent=" * ")}
+ *
+ * python3 {this_file}{alternative_mode}
+ */
+
+/*
+ * Table of {table_description}
+ * Sorted by base character and then combining mark for binary search
+ */
+static const struct ucs_recomposition ucs_recomposition_table[] = {{
+""")
+
+ for base, combining, recomposed in recompose_list:
+ try:
+ base_name = unicodedata.name(chr(base))
+ combining_name = unicodedata.name(chr(combining))
+ recomposed_name = unicodedata.name(chr(recomposed))
+ comment = f"/* {base_name} + {combining_name} = {recomposed_name} */"
+ except ValueError:
+ comment = f"/* U+{base:04X} + U+{combining:04X} = U+{recomposed:04X} */"
+ f.write(f"\t{{ 0x{base:04X}, 0x{combining:04X}, 0x{recomposed:04X} }}, {comment}\n")
+
+ f.write(f"""\
+}};
+
+/*
+ * Boundary values for quick rejection
+ * These are calculated by analyzing the table during generation
+ */
+#define UCS_RECOMPOSE_MIN_BASE 0x{min_base:04X}
+#define UCS_RECOMPOSE_MAX_BASE 0x{max_base:04X}
+#define UCS_RECOMPOSE_MIN_MARK 0x{min_combining:04X}
+#define UCS_RECOMPOSE_MAX_MARK 0x{max_combining:04X}
+""")
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Generate Unicode recomposition table")
+ parser.add_argument("--full", action="store_true",
+ help="Generate a full recomposition table (default: common pairs only)")
+ parser.add_argument("-o", "--output", dest="output_file", default=DEFAULT_OUT_FILE,
+ help=f"Output file name (default: {DEFAULT_OUT_FILE})")
+ args = parser.parse_args()
+
+ generate_recomposition_table(use_full_list=args.full, out_file=args.output_file)
diff --git a/drivers/tty/vt/gen_ucs_width_table.py b/drivers/tty/vt/gen_ucs_width_table.py
new file mode 100755
index 000000000..4d2476842
--- /dev/null
+++ b/drivers/tty/vt/gen_ucs_width_table.py
@@ -0,0 +1,354 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+#
+# Leverage Python's unicodedata module to generate ucs_width_table.h
+
+import unicodedata
+import sys
+import argparse
+
+# This script's file name
+from pathlib import Path
+this_file = Path(__file__).name
+
+# Default output file name
+DEFAULT_OUT_FILE = "ucs_width_table.h"
+
+# --- Global Constants for Width Assignments ---
+
+# Known zero-width characters
+KNOWN_ZERO_WIDTH = (
+ 0x200B, # ZERO WIDTH SPACE
+ 0x200C, # ZERO WIDTH NON-JOINER
+ 0x200D, # ZERO WIDTH JOINER
+ 0x2060, # WORD JOINER
+ 0xFEFF # ZERO WIDTH NO-BREAK SPACE (BOM)
+)
+
+# Zero-width emoji modifiers and components
+# NOTE: Some of these characters would normally be single-width according to
+# East Asian Width properties, but we deliberately override them to be
+# zero-width because they function as modifiers in emoji sequences.
+EMOJI_ZERO_WIDTH = [
+ # Skin tone modifiers
+ (0x1F3FB, 0x1F3FF), # Emoji modifiers (skin tones)
+
+ # Variation selectors (note: VS16 is treated specially in vt.c)
+ (0xFE00, 0xFE0F), # Variation Selectors 1-16
+
+ # Gender and hair style modifiers
+ # These would be single-width by Unicode properties, but are zero-width
+ # when part of emoji
+ (0x2640, 0x2640), # Female sign
+ (0x2642, 0x2642), # Male sign
+ (0x26A7, 0x26A7), # Transgender symbol
+ (0x1F9B0, 0x1F9B3), # Hair components (red, curly, white, bald)
+
+ # Tag characters
+ (0xE0020, 0xE007E), # Tags
+]
+
+# Regional indicators (flag components)
+REGIONAL_INDICATORS = (0x1F1E6, 0x1F1FF) # Regional indicator symbols A-Z
+
+# Double-width emoji ranges
+#
+# Many emoji characters are classified as single-width according to Unicode
+# Standard Annex #11 East Asian Width property (N or Neutral), but we
+# deliberately override them to be double-width. References:
+# 1. Unicode Technical Standard #51: Unicode Emoji
+# (https://www.unicode.org/reports/tr51/)
+# 2. Principle of "emoji presentation" in WHATWG CSS Text specification
+# (https://drafts.csswg.org/css-text-3/#character-properties)
+# 3. Terminal emulator implementations (iTerm2, Windows Terminal, etc.) which
+# universally render emoji as double-width characters regardless of their
+# Unicode EAW property
+# 4. W3C Work Item: Requirements for Japanese Text Layout - Section 3.8.1
+# Emoji width (https://www.w3.org/TR/jlreq/)
+EMOJI_RANGES = [
+ (0x1F000, 0x1F02F), # Mahjong Tiles (EAW: N, but displayed as double-width)
+ (0x1F0A0, 0x1F0FF), # Playing Cards (EAW: N, but displayed as double-width)
+ (0x1F300, 0x1F5FF), # Miscellaneous Symbols and Pictographs
+ (0x1F600, 0x1F64F), # Emoticons
+ (0x1F680, 0x1F6FF), # Transport and Map Symbols
+ (0x1F700, 0x1F77F), # Alchemical Symbols
+ (0x1F780, 0x1F7FF), # Geometric Shapes Extended
+ (0x1F800, 0x1F8FF), # Supplemental Arrows-C
+ (0x1F900, 0x1F9FF), # Supplemental Symbols and Pictographs
+ (0x1FA00, 0x1FA6F), # Chess Symbols
+ (0x1FA70, 0x1FAFF), # Symbols and Pictographs Extended-A
+]
+
+def create_width_tables():
+ """
+ Creates Unicode character width tables and returns the data structures.
+
+ Returns:
+ tuple: (zero_width_ranges, double_width_ranges)
+ """
+
+ # Width data mapping
+ width_map = {} # Maps code points to width (0, 1, 2)
+
+ # Mark emoji modifiers as zero-width
+ for start, end in EMOJI_ZERO_WIDTH:
+ for cp in range(start, end + 1):
+ width_map[cp] = 0
+
+ # Mark all regional indicators as single-width as they are usually paired
+ # providing a combined width of 2 when displayed together.
+ start, end = REGIONAL_INDICATORS
+ for cp in range(start, end + 1):
+ width_map[cp] = 1
+
+ # Process all assigned Unicode code points (Basic Multilingual Plane +
+ # Supplementary Planes) Range 0x0 to 0x10FFFF (the full Unicode range)
+ for block_start in range(0, 0x110000, 0x1000):
+ block_end = block_start + 0x1000
+ for cp in range(block_start, block_end):
+ try:
+ char = chr(cp)
+
+ # Skip if already processed
+ if cp in width_map:
+ continue
+
+ # Check for combining marks and a format characters
+ category = unicodedata.category(char)
+
+ # Combining marks
+ if category.startswith('M'):
+ width_map[cp] = 0
+ continue
+
+ # Format characters
+ # Since we have no support for bidirectional text, all format
+ # characters (category Cf) can be treated with width 0 (zero)
+ # for simplicity, as they don't need to occupy visual space
+ # in a non-bidirectional text environment.
+ if category == 'Cf':
+ width_map[cp] = 0
+ continue
+
+ # Known zero-width characters
+ if cp in KNOWN_ZERO_WIDTH:
+ width_map[cp] = 0
+ continue
+
+ # Use East Asian Width property
+ eaw = unicodedata.east_asian_width(char)
+ if eaw in ('F', 'W'): # Fullwidth or Wide
+ width_map[cp] = 2
+ elif eaw in ('Na', 'H', 'N', 'A'): # Narrow, Halfwidth, Neutral, Ambiguous
+ width_map[cp] = 1
+ else:
+ # Default to single-width for unknown
+ width_map[cp] = 1
+
+ except (ValueError, OverflowError):
+ # Skip invalid code points
+ continue
+
+ # Process Emoji - generally double-width
+ for start, end in EMOJI_RANGES:
+ for cp in range(start, end + 1):
+ if cp not in width_map or width_map[cp] != 0: # Don't override zero-width
+ try:
+ char = chr(cp)
+ width_map[cp] = 2
+ except (ValueError, OverflowError):
+ continue
+
+ # Optimize to create range tables
+ def ranges_optimize(width_data, target_width):
+ points = sorted([cp for cp, width in width_data.items() if width == target_width])
+ if not points:
+ return []
+
+ # Group consecutive code points into ranges
+ ranges = []
+ start = points[0]
+ prev = start
+
+ for cp in points[1:]:
+ if cp > prev + 1:
+ ranges.append((start, prev))
+ start = cp
+ prev = cp
+
+ # Add the last range
+ ranges.append((start, prev))
+ return ranges
+
+ # Extract ranges for each width
+ zero_width_ranges = ranges_optimize(width_map, 0)
+ double_width_ranges = ranges_optimize(width_map, 2)
+
+ return zero_width_ranges, double_width_ranges
+
+def write_tables(zero_width_ranges, double_width_ranges, out_file=DEFAULT_OUT_FILE):
+ """
+ Write the generated tables to C header file.
+
+ The output uses a single sorted-by-`first` table per region (BMP and
+ non-BMP), with zero-width and double-width ranges merged together. The
+ non-BMP table also hosts the BMP double-width bitmap in spare bits of
+ `last`. See the encoding comment at the top of ucs.c for the layout.
+
+ Args:
+ zero_width_ranges: List of (start, end) ranges for zero-width characters
+ double_width_ranges: List of (start, end) ranges for double-width characters
+ out_file: Output file name (default: DEFAULT_OUT_FILE)
+ """
+
+ # Bits per BMP-bitmap chunk hosted in one non-BMP entry's `last` field.
+ # 8 bits makes `idx / BITS_PER_CHUNK` / `idx % BITS_PER_CHUNK` compile to
+ # a cheap shift+mask in the lookup. The chunk size is also emitted as
+ # UCS_NONBMP_BMP_BITS in the generated header so ucs.c stays in sync.
+ BITS_PER_CHUNK = 8
+
+ # Function to split ranges into BMP (16-bit) and non-BMP (above 16-bit)
+ def split_ranges_by_size(ranges):
+ bmp_ranges = []
+ non_bmp_ranges = []
+
+ for start, end in ranges:
+ if end <= 0xFFFF:
+ bmp_ranges.append((start, end))
+ elif start > 0xFFFF:
+ non_bmp_ranges.append((start, end))
+ else:
+ # Split the range at 0xFFFF
+ bmp_ranges.append((start, 0xFFFF))
+ non_bmp_ranges.append((0x10000, end))
+
+ return bmp_ranges, non_bmp_ranges
+
+ # Split ranges into BMP and non-BMP
+ zero_width_bmp, zero_width_non_bmp = split_ranges_by_size(zero_width_ranges)
+ double_width_bmp, double_width_non_bmp = split_ranges_by_size(double_width_ranges)
+
+ # Merge zero- and double-width ranges per region, tagging each with its
+ # width, then sort by `first` so binary search works on the union.
+ bmp_entries = sorted(
+ [(s, e, 0) for s, e in zero_width_bmp] +
+ [(s, e, 2) for s, e in double_width_bmp],
+ key=lambda t: t[0])
+ nonbmp_entries = sorted(
+ [(s, e, 0) for s, e in zero_width_non_bmp] +
+ [(s, e, 2) for s, e in double_width_non_bmp],
+ key=lambda t: t[0])
+
+ # Build the BMP double-width bitmap: one bit per BMP entry (in sort
+ # order), set iff that entry is double-width. Pack into BITS_PER_CHUNK-
+ # wide chunks, with bit j of the chunk corresponding to entry
+ # (chunk_index * BITS_PER_CHUNK + j).
+ bmp_w2_bits = [1 if w == 2 else 0 for _, _, w in bmp_entries]
+ n_chunks = (len(bmp_w2_bits) + BITS_PER_CHUNK - 1) // BITS_PER_CHUNK
+
+ if n_chunks > len(nonbmp_entries):
+ raise RuntimeError(
+ f"BMP bitmap needs {n_chunks} host entries, "
+ f"but only {len(nonbmp_entries)} non-BMP entries are available")
+
+ chunks = [] # list of (base_index, end_index, packed_value)
+ for c in range(n_chunks):
+ base = c * BITS_PER_CHUNK
+ end_idx = min(base + BITS_PER_CHUNK - 1, len(bmp_w2_bits) - 1)
+ value = 0
+ for j in range(BITS_PER_CHUNK):
+ k = base + j
+ if k < len(bmp_w2_bits) and bmp_w2_bits[k]:
+ value |= 1 << j
+ chunks.append((base, end_idx, value))
+
+ # Function to generate code point description comments
+ def get_code_point_comment(start, end):
+ try:
+ start_char_desc = unicodedata.name(chr(start))
+ if start == end:
+ return f"/* {start_char_desc} */"
+ else:
+ end_char_desc = unicodedata.name(chr(end))
+ return f"/* {start_char_desc} - {end_char_desc} */"
+ except:
+ if start == end:
+ return f"/* U+{start:04X} */"
+ else:
+ return f"/* U+{start:04X} - U+{end:04X} */"
+
+ # Generate C tables
+ with open(out_file, 'w') as f:
+ f.write(f"""\
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * {out_file} - Unicode character width
+ *
+ * Auto-generated by {this_file}
+ *
+ * Unicode Version: {unicodedata.unidata_version}
+ *
+ * Zero-width and double-width ranges are merged into one sorted-by-`first`
+ * table per region. The non-BMP table additionally hosts the BMP
+ * double-width bitmap in the low {BITS_PER_CHUNK} bits of `last` of its
+ * first {n_chunks} entries (covering {len(bmp_w2_bits)} BMP entries).
+ * See ucs.c for the encoding details and the lookup code.
+ */
+
+/* Bits per BMP-bitmap chunk hosted in one non-BMP entry's `last` field. */
+#define UCS_NONBMP_BMP_BITS {BITS_PER_CHUNK}
+
+/* Combined zero- and double-width ranges
+ * (BMP - Basic Multilingual Plane, U+0000 to U+FFFF). */
+static const struct ucs_width16 ucs_bmp_ranges[] = {{
+""")
+
+ for s, e, w in bmp_entries:
+ macro = "BMP_0WIDTH" if w == 0 else "BMP_2WIDTH"
+ comment = get_code_point_comment(s, e)
+ f.write(f"\t{{ {macro}(0x{s:04X}, 0x{e:04X}) }}, {comment}\n")
+
+ f.write(f"""\
+}};
+
+/* Combined zero- and double-width ranges (non-BMP, U+10000 and above).
+ * The first {n_chunks} entries host the BMP double-width bitmap in the low
+ * {BITS_PER_CHUNK} bits of `last`. */
+static const struct ucs_width32 ucs_nonbmp_ranges[] = {{
+""")
+
+ for i, (s, e, w) in enumerate(nonbmp_entries):
+ macro = "RANGE_0WIDTH" if w == 0 else "RANGE_2WIDTH"
+ comment = get_code_point_comment(s, e)
+ if i < len(chunks):
+ base, end_idx, value = chunks[i]
+ f.write(
+ f"\t{{ {macro}(0x{s:05X}, 0x{e:05X}) {comment}\n"
+ f"\t | BMP_2W_BITS(0b{value:0{BITS_PER_CHUNK}b}) }},"
+ f" /* BMP entries [{base:>3}..{end_idx:>3}] */\n")
+ else:
+ f.write(f"\t{{ {macro}(0x{s:05X}, 0x{e:05X}) }}, {comment}\n")
+
+ f.write("};\n")
+
+if __name__ == "__main__":
+ # Parse command line arguments
+ parser = argparse.ArgumentParser(description="Generate Unicode width tables")
+ parser.add_argument("-o", "--output", dest="output_file", default=DEFAULT_OUT_FILE,
+ help=f"Output file name (default: {DEFAULT_OUT_FILE})")
+ args = parser.parse_args()
+
+ # Write tables to header file
+ zero_width_ranges, double_width_ranges = create_width_tables()
+ write_tables(zero_width_ranges, double_width_ranges, out_file=args.output_file)
+
+ # Print summary
+ zero_width_count = sum(end - start + 1 for start, end in zero_width_ranges)
+ double_width_count = sum(end - start + 1 for start, end in double_width_ranges)
+ n_zero = len(zero_width_ranges)
+ n_double = len(double_width_ranges)
+ print(f"Generated {args.output_file} with:")
+ print(f"- {n_zero} zero-width ranges covering ~{zero_width_count} code points")
+ print(f"- {n_double} double-width ranges covering ~{double_width_count} code points")
+ print(f"- {n_zero + n_double} merged ranges total")
+ print(f"- Unicode Version: {unicodedata.unidata_version}")
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
new file mode 100644
index 000000000..c41d850b2
--- /dev/null
+++ b/drivers/tty/vt/keyboard.c
@@ -0,0 +1,2291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Written for linux by Johan Myreen as a translation from
+ * the assembly version by Linus (with diacriticals added)
+ *
+ * Some additional features added by Christoph Niemann (ChN), March 1993
+ *
+ * Loadable keymaps by Risto Kankkunen, May 1993
+ *
+ * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
+ * Added decr/incr_console, dynamic keymaps, Unicode support,
+ * dynamic function/string keys, led setting, Sept 1994
+ * `Sticky' modifier keys, 951006.
+ *
+ * 11-11-96: SAK should now work in the raw mode (Martin Mares)
+ *
+ * Modified to provide 'generic' keyboard support by Hamish Macdonald
+ * Merge with the m68k keyboard driver and split-off of the PC low-level
+ * parts by Geert Uytterhoeven, May 1997
+ *
+ * 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
+ * 30-07-98: Dead keys redone, aeb@cwi.nl.
+ * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/consolemap.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+#include <linux/kbd_diacr.h>
+#include <linux/kbd_kern.h>
+#include <linux/leds.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/nospec.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/sched/debug.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/uaccess.h>
+#include <linux/vt_kern.h>
+
+#include <asm/irq_regs.h>
+
+/*
+ * Exported functions/variables
+ */
+
+#define KBD_DEFMODE (BIT(VC_REPEAT) | BIT(VC_META))
+
+#if defined(CONFIG_X86) || defined(CONFIG_PARISC)
+#include <asm/kbdleds.h>
+#else
+static inline int kbd_defleds(void)
+{
+ return 0;
+}
+#endif
+
+#define KBD_DEFLOCK 0
+
+/*
+ * 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_csi
+
+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
+ */
+
+struct vt_spawn_console vt_spawn_con = {
+ .lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock),
+ .pid = NULL,
+ .sig = 0,
+};
+
+
+/*
+ * Internal Data.
+ */
+
+static struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+static struct kbd_struct *kbd = kbd_table;
+
+/* maximum values each key_handler can handle */
+static const unsigned char max_vals[] = {
+ [ KT_LATIN ] = 255,
+ [ KT_FN ] = ARRAY_SIZE(func_table) - 1,
+ [ KT_SPEC ] = ARRAY_SIZE(fn_handler) - 1,
+ [ KT_PAD ] = NR_PAD - 1,
+ [ KT_DEAD ] = NR_DEAD - 1,
+ [ KT_CONS ] = 255,
+ [ KT_CUR ] = 3,
+ [ KT_SHIFT ] = NR_SHIFT - 1,
+ [ KT_META ] = 255,
+ [ KT_ASCII ] = NR_ASCII - 1,
+ [ KT_LOCK ] = NR_LOCK - 1,
+ [ KT_LETTER ] = 255,
+ [ KT_SLOCK ] = NR_LOCK - 1,
+ [ KT_DEAD2 ] = 255,
+ [ KT_BRL ] = NR_BRL - 1,
+ [ KT_CSI ] = 99,
+};
+
+static const int NR_TYPES = ARRAY_SIZE(max_vals);
+
+static void kbd_bh(struct tasklet_struct *unused);
+static DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh);
+
+static struct input_handler kbd_handler;
+static DEFINE_SPINLOCK(kbd_event_lock);
+static DEFINE_SPINLOCK(led_lock);
+static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
+static DECLARE_BITMAP(key_down, KEY_CNT); /* keyboard key bitmap */
+static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
+static bool dead_key_next;
+
+/* Handles a number being assembled on the number pad */
+static bool npadch_active;
+static unsigned int npadch_value;
+
+static unsigned int diacr;
+static bool rep; /* flag telling character repeat */
+
+static int shift_state = 0;
+
+static unsigned int ledstate = -1U; /* undefined */
+static unsigned char ledioctl;
+static bool vt_switch;
+
+/*
+ * 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 */
+}
+
+static 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 */
+}
+
+static 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(struct timer_list *unused)
+{
+ static unsigned int zero;
+
+ input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
+}
+
+static DEFINE_TIMER(kd_mksound_timer, kd_nosound);
+
+void kd_mksound(unsigned int hz, unsigned int ticks)
+{
+ timer_delete_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 *rpt = data;
+
+ if (test_bit(EV_REP, dev->evbit)) {
+
+ if (rpt[0].delay > 0)
+ input_inject_event(handle,
+ EV_REP, REP_DELAY, rpt[0].delay);
+ if (rpt[0].period > 0)
+ input_inject_event(handle,
+ EV_REP, REP_PERIOD, rpt[0].period);
+
+ rpt[1].delay = dev->rep[REP_DELAY];
+ rpt[1].period = dev->rep[REP_PERIOD];
+ }
+
+ return 0;
+}
+
+int kbd_rate(struct kbd_repeat *rpt)
+{
+ struct kbd_repeat data[2] = { *rpt };
+
+ input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
+ *rpt = data[1]; /* Copy currently used settings */
+
+ return 0;
+}
+
+/*
+ * Helper Functions.
+ */
+static void put_queue(struct vc_data *vc, int ch)
+{
+ tty_insert_flip_char(&vc->port, ch, 0);
+ tty_flip_buffer_push(&vc->port);
+}
+
+static void puts_queue(struct vc_data *vc, const char *cp)
+{
+ tty_insert_flip_string(&vc->port, cp, strlen(cp));
+ tty_flip_buffer_push(&vc->port);
+}
+
+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));
+ }
+}
+
+static void put_queue_utf8(struct vc_data *vc, u32 value)
+{
+ if (kbd->kbdmode == VC_UNICODE)
+ to_utf8(vc, value);
+ else {
+ int c = conv_uni_to_8bit(value);
+ if (c != -1)
+ put_queue(vc, c);
+ }
+}
+
+/* FIXME: review locking for vt.c callers */
+static void set_leds(void)
+{
+ tasklet_schedule(&keyboard_tasklet);
+}
+
+/*
+ * 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. The caller must hold the
+ * kbd_event_lock.
+ */
+
+static void do_compute_shiftstate(void)
+{
+ unsigned int k, sym, val;
+
+ shift_state = 0;
+ memset(shift_down, 0, sizeof(shift_down));
+
+ for_each_set_bit(k, key_down, min(NR_KEYS, KEY_CNT)) {
+ 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 |= BIT(val);
+ }
+}
+
+/* We still have to export this method to vt.c */
+void vt_set_leds_compute_shiftstate(void)
+{
+ /*
+ * When VT is switched, the keyboard led needs to be set once.
+ * Ensure that after the switch is completed, the state of the
+ * keyboard LED is consistent with the state of the keyboard lock.
+ */
+ vt_switch = true;
+ set_leds();
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ do_compute_shiftstate();
+}
+
+/*
+ * 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;
+
+ put_queue_utf8(vc, d);
+
+ return ch;
+}
+
+/*
+ * Special function handlers
+ */
+static void fn_enter(struct vc_data *vc)
+{
+ if (diacr) {
+ put_queue_utf8(vc, diacr);
+ diacr = 0;
+ }
+
+ put_queue(vc, '\r');
+ if (vc_kbd_mode(kbd, VC_CRLF))
+ put_queue(vc, '\n');
+}
+
+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->flow.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)
+{
+ tty_insert_flip_char(&vc->port, 0, TTY_BREAK);
+ tty_flip_buffer_push(&vc->port);
+}
+
+static void fn_scroll_forw(struct vc_data *vc)
+{
+ scrollfront(vc, 0);
+}
+
+static void fn_scroll_back(struct vc_data *vc)
+{
+ scrollback(vc);
+}
+
+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)
+{
+ guard(spinlock)(&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;
+ }
+}
+
+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)
+{
+ do_compute_shiftstate();
+}
+
+/*
+ * Special key handlers
+ */
+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 ||
+ kbd->kbdmode == VC_OFF) &&
+ 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;
+ }
+ put_queue_utf8(vc, value);
+}
+
+/*
+ * 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] = {
+ '`', /* dead_grave */
+ '\'', /* dead_acute */
+ '^', /* dead_circumflex */
+ '~', /* dead_tilda */
+ '"', /* dead_diaeresis */
+ ',', /* dead_cedilla */
+ '_', /* dead_macron */
+ 'U', /* dead_breve */
+ '.', /* dead_abovedot */
+ '*', /* dead_abovering */
+ '=', /* dead_doubleacute */
+ 'c', /* dead_caron */
+ 'k', /* dead_ogonek */
+ 'i', /* dead_iota */
+ '#', /* dead_voiced_sound */
+ 'o', /* dead_semivoiced_sound */
+ '!', /* dead_belowdot */
+ '?', /* dead_hook */
+ '+', /* dead_horn */
+ '-', /* dead_stroke */
+ ')', /* dead_abovecomma */
+ '(', /* dead_abovereversedcomma */
+ ':', /* dead_doublegrave */
+ 'n', /* dead_invertedbreve */
+ ';', /* dead_belowcomma */
+ '$', /* dead_currency */
+ '@', /* dead_greek */
+ };
+
+ 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)) {
+ guard(spinlock_irqsave)(&func_buf_lock);
+ if (func_table[value])
+ puts_queue(vc, func_table[value]);
+ } else
+ pr_err("k_fn called with value=%d\n", value);
+}
+
+/*
+ * Compute xterm-style modifier parameter for CSI sequences.
+ * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0)
+ *
+ * Only the canonical modifier weights are counted. The left/right variants
+ * (KG_SHIFTL, KG_SHIFTR, KG_CTRLL, KG_CTRLR) and KG_ALTGR are commonly
+ * repurposed as keymap layout-group or level selectors rather than as plain
+ * modifiers (for instance XKB-derived keymaps select the layout group with
+ * KG_SHIFTL/KG_SHIFTR), so counting them would encode a spurious modifier.
+ */
+static int csi_modifier_param(void)
+{
+ int mod = 1;
+
+ if (shift_state & BIT(KG_SHIFT))
+ mod += 1;
+ if (shift_state & BIT(KG_ALT))
+ mod += 2;
+ if (shift_state & BIT(KG_CTRL))
+ mod += 4;
+ return mod;
+}
+
+static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ static const char cur_chars[] = "BDCA";
+ int mod;
+
+ if (up_flag)
+ return;
+
+ mod = csi_modifier_param();
+ if (mod > 1) {
+ char buf[] = { 0x1b, '[', '1', ';', '0' + mod, cur_chars[value], 0x00 };
+
+ puts_queue(vc, buf);
+ } else {
+ 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, '\n');
+}
+
+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 |= BIT(value);
+ else
+ shift_state &= ~BIT(value);
+
+ /* kludge */
+ if (up_flag && shift_state != old_state && npadch_active) {
+ if (kbd->kbdmode == VC_UNICODE)
+ to_utf8(vc, npadch_value);
+ else
+ put_queue(vc, npadch_value & 0xff);
+ npadch_active = false;
+ }
+}
+
+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 | BIT(7));
+}
+
+static void k_ascii(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ unsigned 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_active) {
+ npadch_value = 0;
+ npadch_active = true;
+ }
+
+ npadch_value = npadch_value * 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_warn("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 |= BIT(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 &= ~BIT(value - 1);
+ if (!pressed && committing) {
+ k_brlcommit(vc, committing, 0);
+ committing = 0;
+ }
+ } else {
+ if (committing) {
+ k_brlcommit(vc, committing, 0);
+ committing = 0;
+ }
+ pressed &= ~BIT(value - 1);
+ }
+}
+
+/*
+ * Handle KT_CSI keysym type: generate CSI tilde sequences with modifier
+ * support. The value encodes the CSI parameter number, producing sequences
+ * like ESC [ <value> ~ or ESC [ <value> ; <mod> ~ when modifiers are held.
+ */
+static void k_csi(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ char buf[10];
+ int i = 0;
+ int mod;
+
+ if (up_flag)
+ return;
+
+ mod = csi_modifier_param();
+
+ buf[i++] = 0x1b;
+ buf[i++] = '[';
+ if (value >= 10)
+ buf[i++] = '0' + value / 10;
+ buf[i++] = '0' + value % 10;
+ if (mod > 1) {
+ buf[i++] = ';';
+ buf[i++] = '0' + mod;
+ }
+ buf[i++] = '~';
+ buf[i] = 0x00;
+
+ puts_queue(vc, buf);
+}
+
+#if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS)
+
+struct kbd_led_trigger {
+ struct led_trigger trigger;
+ unsigned int mask;
+};
+
+static int kbd_led_trigger_activate(struct led_classdev *cdev)
+{
+ struct kbd_led_trigger *trigger =
+ container_of(cdev->trigger, struct kbd_led_trigger, trigger);
+
+ tasklet_disable(&keyboard_tasklet);
+ if (ledstate != -1U)
+ led_set_brightness(cdev, ledstate & trigger->mask ? LED_FULL : LED_OFF);
+ tasklet_enable(&keyboard_tasklet);
+
+ return 0;
+}
+
+#define KBD_LED_TRIGGER(_led_bit, _name) { \
+ .trigger = { \
+ .name = _name, \
+ .activate = kbd_led_trigger_activate, \
+ }, \
+ .mask = BIT(_led_bit), \
+ }
+
+#define KBD_LOCKSTATE_TRIGGER(_led_bit, _name) \
+ KBD_LED_TRIGGER((_led_bit) + 8, _name)
+
+static struct kbd_led_trigger kbd_led_triggers[] = {
+ KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"),
+ KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"),
+ KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"),
+ KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"),
+
+ KBD_LOCKSTATE_TRIGGER(VC_SHIFTLOCK, "kbd-shiftlock"),
+ KBD_LOCKSTATE_TRIGGER(VC_ALTGRLOCK, "kbd-altgrlock"),
+ KBD_LOCKSTATE_TRIGGER(VC_CTRLLOCK, "kbd-ctrllock"),
+ KBD_LOCKSTATE_TRIGGER(VC_ALTLOCK, "kbd-altlock"),
+ KBD_LOCKSTATE_TRIGGER(VC_SHIFTLLOCK, "kbd-shiftllock"),
+ KBD_LOCKSTATE_TRIGGER(VC_SHIFTRLOCK, "kbd-shiftrlock"),
+ KBD_LOCKSTATE_TRIGGER(VC_CTRLLLOCK, "kbd-ctrlllock"),
+ KBD_LOCKSTATE_TRIGGER(VC_CTRLRLOCK, "kbd-ctrlrlock"),
+};
+
+static void kbd_propagate_led_state(unsigned int old_state,
+ unsigned int new_state)
+{
+ struct kbd_led_trigger *trigger;
+ unsigned int changed = old_state ^ new_state;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) {
+ trigger = &kbd_led_triggers[i];
+
+ if (changed & trigger->mask)
+ led_trigger_event(&trigger->trigger,
+ new_state & trigger->mask ?
+ LED_FULL : LED_OFF);
+ }
+}
+
+static int kbd_update_leds_helper(struct input_handle *handle, void *data)
+{
+ unsigned int led_state = *(unsigned int *)data;
+
+ if (test_bit(EV_LED, handle->dev->evbit))
+ kbd_propagate_led_state(~led_state, led_state);
+
+ return 0;
+}
+
+static void kbd_init_leds(void)
+{
+ int error;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) {
+ error = led_trigger_register(&kbd_led_triggers[i].trigger);
+ if (error)
+ pr_err("error %d while registering trigger %s\n",
+ error, kbd_led_triggers[i].trigger.name);
+ }
+}
+
+#else
+
+static int kbd_update_leds_helper(struct input_handle *handle, void *data)
+{
+ unsigned int leds = *(unsigned int *)data;
+
+ if (test_bit(EV_LED, handle->dev->evbit)) {
+ input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & BIT(0)));
+ input_inject_event(handle, EV_LED, LED_NUML, !!(leds & BIT(1)));
+ input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & BIT(2)));
+ input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
+ }
+
+ return 0;
+}
+
+static void kbd_propagate_led_state(unsigned int old_state,
+ unsigned int new_state)
+{
+ input_handler_for_each_handle(&kbd_handler, &new_state,
+ kbd_update_leds_helper);
+}
+
+static void kbd_init_leds(void)
+{
+}
+
+#endif
+
+/*
+ * 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.
+ */
+static unsigned char getledstate(void)
+{
+ return ledstate & 0xff;
+}
+
+void setledstate(struct kbd_struct *kb, unsigned int led)
+{
+ guard(spinlock_irqsave)(&led_lock);
+ if (!(led & ~7)) {
+ ledioctl = led;
+ kb->ledmode = LED_SHOW_IOCTL;
+ } else
+ kb->ledmode = LED_SHOW_FLAGS;
+
+ set_leds();
+}
+
+static inline unsigned char getleds(void)
+{
+ struct kbd_struct *kb = kbd_table + fg_console;
+
+ if (kb->ledmode == LED_SHOW_IOCTL)
+ return ledioctl;
+
+ return kb->ledflagstate;
+}
+
+/**
+ * vt_get_leds - helper for braille console
+ * @console: console to read
+ * @flag: flag we want to check
+ *
+ * Check the status of a keyboard led flag and report it back
+ */
+int vt_get_leds(unsigned int console, int flag)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&led_lock);
+ return vc_kbd_led(kb, flag);
+}
+EXPORT_SYMBOL_GPL(vt_get_leds);
+
+/**
+ * vt_set_led_state - set LED state of a console
+ * @console: console to set
+ * @leds: LED bits
+ *
+ * Set the LEDs on a console. This is a wrapper for the VT layer
+ * so that we can keep kbd knowledge internal
+ */
+void vt_set_led_state(unsigned int console, int leds)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+ setledstate(kb, leds);
+}
+
+/**
+ * vt_kbd_con_start - Keyboard side of console start
+ * @console: console
+ *
+ * Handle console start. This is a wrapper for the VT layer
+ * so that we can keep kbd knowledge internal
+ *
+ * FIXME: We eventually need to hold the kbd lock here to protect
+ * the LED updating. We can't do it yet because fn_hold calls stop_tty
+ * and start_tty under the kbd_event_lock, while normal tty paths
+ * don't hold the lock. We probably need to split out an LED lock
+ * but not during an -rc release!
+ */
+void vt_kbd_con_start(unsigned int console)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&led_lock);
+ clr_vc_kbd_led(kb, VC_SCROLLOCK);
+ set_leds();
+}
+
+/**
+ * vt_kbd_con_stop - Keyboard side of console stop
+ * @console: console
+ *
+ * Handle console stop. This is a wrapper for the VT layer
+ * so that we can keep kbd knowledge internal
+ */
+void vt_kbd_con_stop(unsigned int console)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&led_lock);
+ set_vc_kbd_led(kb, VC_SCROLLOCK);
+ set_leds();
+}
+
+/*
+ * This is the tasklet that updates LED state of LEDs using standard
+ * keyboard triggers. 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 from the VT to update led state.
+ */
+static void kbd_bh(struct tasklet_struct *unused)
+{
+ unsigned int leds;
+
+ scoped_guard(spinlock_irqsave, &led_lock) {
+ leds = getleds();
+ leds |= (unsigned int)kbd->lockstate << 8;
+ }
+
+ if (vt_switch) {
+ ledstate = ~leds;
+ vt_switch = false;
+ }
+
+ if (leds != ledstate) {
+ kbd_propagate_led_state(ledstate, leds);
+ ledstate = leds;
+ }
+}
+
+#if defined(CONFIG_X86) || 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))
+
+static inline bool kbd_is_hw_raw(const struct input_dev *dev)
+{
+ if (!test_bit(EV_MSC, dev->evbit) || !test_bit(MSC_RAW, dev->mscbit))
+ return false;
+
+ return 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
+
+static inline bool kbd_is_hw_raw(const struct input_dev *dev)
+{
+ return false;
+}
+
+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, bool 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 = tty_port_tty_get(&vc->port);
+
+ 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_warn("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) | BIT(7));
+ put_queue(vc, keycode | BIT(7));
+ }
+ raw_mode = true;
+ }
+
+ assign_bit(keycode, key_down, 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.
+ */
+ tty_kref_put(tty);
+ return;
+ }
+
+ tty_kref_put(tty);
+
+ param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
+ param.ledstate = kbd->ledflagstate;
+ key_map = key_maps[shift_final];
+
+ /*
+ * Fall back to the plain map if modifiers are active, the modifier-
+ * specific map is missing or has no entry, and the plain map has a
+ * modifier-aware key type (KT_CUR or KT_CSI). These handlers encode
+ * the modifier state into the emitted escape sequence.
+ */
+ if (shift_final && keycode < NR_KEYS &&
+ (!key_map || key_map[keycode] == K_HOLE) && key_maps[0]) {
+ unsigned short plain = key_maps[0][keycode];
+ unsigned char type = KTYP(plain);
+
+ if (type >= 0xf0 && (type - 0xf0 == KT_CUR || type - 0xf0 == KT_CSI))
+ key_map = key_maps[0];
+ }
+
+ rc = atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_KEYCODE, &param);
+ if (rc == NOTIFY_STOP || !key_map) {
+ atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_UNBOUND_KEYCODE, &param);
+ do_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, &param);
+ if (rc != NOTIFY_STOP)
+ if (down && !(raw_mode || kbd->kbdmode == VC_OFF))
+ k_unicode(vc, keysym, !down);
+ return;
+ }
+
+ type -= 0xf0;
+
+ if (type == KT_LETTER) {
+ type = KT_LATIN;
+ if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
+ key_map = key_maps[shift_final ^ BIT(KG_SHIFT)];
+ if (key_map)
+ keysym = key_map[keycode];
+ }
+ }
+
+ param.value = keysym;
+ rc = atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_KEYSYM, &param);
+ if (rc == NOTIFY_STOP)
+ return;
+
+ if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT)
+ return;
+
+ (*k_handler[type])(vc, KVAL(keysym), !down);
+
+ param.ledstate = kbd->ledflagstate;
+ atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, &param);
+
+ 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 */
+ scoped_guard(spinlock, &kbd_event_lock) {
+ if (event_type == EV_MSC && event_code == MSC_RAW &&
+ kbd_is_hw_raw(handle->dev))
+ kbd_rawcode(value);
+ if (event_type == EV_KEY && event_code <= KEY_MAX)
+ kbd_keycode(event_code, value, kbd_is_hw_raw(handle->dev));
+ }
+
+ tasklet_schedule(&keyboard_tasklet);
+ do_poke_blanked_console = 1;
+ schedule_console_callback();
+}
+
+static bool kbd_match(struct input_handler *handler, struct input_dev *dev)
+{
+ if (test_bit(EV_SND, dev->evbit))
+ return true;
+
+ if (test_bit(EV_KEY, dev->evbit)) {
+ if (find_next_bit(dev->keybit, BTN_MISC, KEY_RESERVED) <
+ BTN_MISC)
+ return true;
+ if (find_next_bit(dev->keybit, KEY_BRL_DOT10 + 1,
+ KEY_BRL_DOT1) <= KEY_BRL_DOT10)
+ 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)
+{
+ int error;
+
+ struct input_handle __free(kfree) *handle = kzalloc_obj(*handle);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "kbd";
+
+ error = input_register_handle(handle);
+ if (error)
+ return error;
+
+ error = input_open_device(handle);
+ if (error)
+ goto err_unregister_handle;
+
+ retain_and_null_ptr(handle);
+
+ return 0;
+
+ err_unregister_handle:
+ input_unregister_handle(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 != -1U)
+ 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;
+ }
+
+ kbd_init_leds();
+
+ error = input_register_handler(&kbd_handler);
+ if (error)
+ return error;
+
+ tasklet_enable(&keyboard_tasklet);
+ tasklet_schedule(&keyboard_tasklet);
+
+ return 0;
+}
+
+/* Ioctl support code */
+
+static int vt_do_kdgkbdiacr(void __user *udp)
+{
+ struct kbdiacrs __user *a = udp;
+ int i, asize;
+
+ struct kbdiacr __free(kfree) *dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
+ GFP_KERNEL);
+ if (!dia)
+ return -ENOMEM;
+
+ /* Lock the diacriticals table, make a copy and then
+ copy it after we unlock */
+ scoped_guard(spinlock_irqsave, &kbd_event_lock) {
+ asize = accent_table_size;
+ for (i = 0; i < asize; i++) {
+ dia[i].diacr = conv_uni_to_8bit(accent_table[i].diacr);
+ dia[i].base = conv_uni_to_8bit(accent_table[i].base);
+ dia[i].result = conv_uni_to_8bit(accent_table[i].result);
+ }
+ }
+
+ if (put_user(asize, &a->kb_cnt))
+ return -EFAULT;
+ if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr)))
+ return -EFAULT;
+ return 0;
+}
+
+static int vt_do_kdgkbdiacruc(void __user *udp)
+{
+ struct kbdiacrsuc __user *a = udp;
+ int asize;
+
+ void __free(kfree) *buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
+ GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Lock the diacriticals table, make a copy and then
+ copy it after we unlock */
+ scoped_guard(spinlock_irqsave, &kbd_event_lock) {
+ asize = accent_table_size;
+ memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
+ }
+
+ if (put_user(asize, &a->kb_cnt))
+ return -EFAULT;
+ if (copy_to_user(a->kbdiacruc, buf, asize * sizeof(struct kbdiacruc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int vt_do_kdskbdiacr(void __user *udp, int perm)
+{
+ struct kbdiacrs __user *a = udp;
+ struct kbdiacr __free(kfree) *dia = NULL;
+ unsigned int ct;
+ int i;
+
+ if (!perm)
+ return -EPERM;
+ if (get_user(ct, &a->kb_cnt))
+ return -EFAULT;
+ if (ct >= MAX_DIACR)
+ return -EINVAL;
+
+ if (ct) {
+ dia = memdup_array_user(a->kbdiacr,
+ ct, sizeof(struct kbdiacr));
+ if (IS_ERR(dia))
+ return PTR_ERR(dia);
+ }
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ accent_table_size = ct;
+ for (i = 0; i < ct; i++) {
+ accent_table[i].diacr =
+ conv_8bit_to_uni(dia[i].diacr);
+ accent_table[i].base =
+ conv_8bit_to_uni(dia[i].base);
+ accent_table[i].result =
+ conv_8bit_to_uni(dia[i].result);
+ }
+
+ return 0;
+}
+
+static int vt_do_kdskbdiacruc(void __user *udp, int perm)
+{
+ struct kbdiacrsuc __user *a = udp;
+ unsigned int ct;
+ void __free(kfree) *buf = NULL;
+
+ if (!perm)
+ return -EPERM;
+
+ if (get_user(ct, &a->kb_cnt))
+ return -EFAULT;
+
+ if (ct >= MAX_DIACR)
+ return -EINVAL;
+
+ if (ct) {
+ buf = memdup_array_user(a->kbdiacruc,
+ ct, sizeof(struct kbdiacruc));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+ }
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ if (ct)
+ memcpy(accent_table, buf,
+ ct * sizeof(struct kbdiacruc));
+ accent_table_size = ct;
+ return 0;
+}
+
+/**
+ * vt_do_diacrit - diacritical table updates
+ * @cmd: ioctl request
+ * @udp: pointer to user data for ioctl
+ * @perm: permissions check computed by caller
+ *
+ * Update the diacritical tables atomically and safely. Lock them
+ * against simultaneous keypresses
+ */
+int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
+{
+ switch (cmd) {
+ case KDGKBDIACR:
+ return vt_do_kdgkbdiacr(udp);
+ case KDGKBDIACRUC:
+ return vt_do_kdgkbdiacruc(udp);
+ case KDSKBDIACR:
+ return vt_do_kdskbdiacr(udp, perm);
+ case KDSKBDIACRUC:
+ return vt_do_kdskbdiacruc(udp, perm);
+ }
+ return 0;
+}
+
+/**
+ * vt_do_kdskbmode - set keyboard mode ioctl
+ * @console: the console to use
+ * @arg: the requested mode
+ *
+ * Update the keyboard mode bits while holding the correct locks.
+ * Return 0 for success or an error code.
+ */
+int vt_do_kdskbmode(unsigned int console, unsigned int arg)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ switch(arg) {
+ case K_RAW:
+ kb->kbdmode = VC_RAW;
+ return 0;
+ case K_MEDIUMRAW:
+ kb->kbdmode = VC_MEDIUMRAW;
+ return 0;
+ case K_XLATE:
+ kb->kbdmode = VC_XLATE;
+ do_compute_shiftstate();
+ return 0;
+ case K_UNICODE:
+ kb->kbdmode = VC_UNICODE;
+ do_compute_shiftstate();
+ return 0;
+ case K_OFF:
+ kb->kbdmode = VC_OFF;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * vt_do_kdskbmeta - set keyboard meta state
+ * @console: the console to use
+ * @arg: the requested meta state
+ *
+ * Update the keyboard meta bits while holding the correct locks.
+ * Return 0 for success or an error code.
+ */
+int vt_do_kdskbmeta(unsigned int console, unsigned int arg)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ switch(arg) {
+ case K_METABIT:
+ clr_vc_kbd_mode(kb, VC_META);
+ return 0;
+ case K_ESCPREFIX:
+ set_vc_kbd_mode(kb, VC_META);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm)
+{
+ struct kbkeycode tmp;
+ int kc;
+
+ if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case KDGETKEYCODE:
+ kc = getkeycode(tmp.scancode);
+ if (kc < 0)
+ return kc;
+ return put_user(kc, &user_kbkc->keycode);
+ case KDSETKEYCODE:
+ if (!perm)
+ return -EPERM;
+ return setkeycode(tmp.scancode, tmp.keycode);
+ }
+
+ return 0;
+}
+
+static unsigned short vt_kdgkbent(unsigned char kbdmode, unsigned char idx,
+ unsigned char map)
+{
+ unsigned short *key_map;
+
+ /* Ensure another thread doesn't free it under us */
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ key_map = key_maps[map];
+ if (key_map) {
+ unsigned short val = U(key_map[idx]);
+ if (kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+ return K_HOLE;
+ return val;
+ }
+
+ return idx ? K_HOLE : K_NOSUCHMAP;
+}
+
+static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
+ unsigned char map, unsigned short val)
+{
+ unsigned short *key_map, oldval;
+
+ if (!idx && val == K_NOSUCHMAP) {
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ /* deallocate map */
+ key_map = key_maps[map];
+ if (map && key_map) {
+ key_maps[map] = NULL;
+ if (key_map[0] == U(K_ALLOCATED)) {
+ kfree(key_map);
+ keymap_count--;
+ }
+ }
+
+ return 0;
+ }
+
+ if (KTYP(val) < NR_TYPES) {
+ if (KVAL(val) > max_vals[KTYP(val)])
+ return -EINVAL;
+ } else if (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 (!idx)
+ return 0;
+#endif
+
+ unsigned short __free(kfree) *new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ key_map = key_maps[map];
+ if (key_map == NULL) {
+ int j;
+
+ if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ key_map = key_maps[map] = no_free_ptr(new_map);
+ key_map[0] = U(K_ALLOCATED);
+ for (j = 1; j < NR_KEYS; j++)
+ key_map[j] = U(K_HOLE);
+ keymap_count++;
+ }
+
+ oldval = U(key_map[idx]);
+ if (val == oldval)
+ return 0;
+
+ /* Attention Key */
+ if ((oldval == K_SAK || val == K_SAK) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ key_map[idx] = U(val);
+ if (!map && (KTYP(oldval) == KT_SHIFT || KTYP(val) == KT_SHIFT))
+ do_compute_shiftstate();
+
+ return 0;
+}
+
+int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
+ unsigned int console)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+ struct kbentry kbe;
+
+ if (copy_from_user(&kbe, user_kbe, sizeof(struct kbentry)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case KDGKBENT:
+ return put_user(vt_kdgkbent(kb->kbdmode, kbe.kb_index,
+ kbe.kb_table),
+ &user_kbe->kb_value);
+ case KDSKBENT:
+ if (!perm || !capable(CAP_SYS_TTY_CONFIG))
+ return -EPERM;
+ return vt_kdskbent(kb->kbdmode, kbe.kb_index, kbe.kb_table,
+ kbe.kb_value);
+ }
+ return 0;
+}
+
+static char *vt_kdskbsent(char *kbs, unsigned char cur)
+{
+ static DECLARE_BITMAP(is_kmalloc, MAX_NR_FUNC);
+ char *cur_f = func_table[cur];
+
+ if (cur_f && strlen(cur_f) >= strlen(kbs)) {
+ strcpy(cur_f, kbs);
+ return kbs;
+ }
+
+ func_table[cur] = kbs;
+
+ return __test_and_set_bit(cur, is_kmalloc) ? cur_f : NULL;
+}
+
+int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
+{
+ unsigned char kb_func;
+
+ if (get_user(kb_func, &user_kdgkb->kb_func))
+ return -EFAULT;
+
+ kb_func = array_index_nospec(kb_func, MAX_NR_FUNC);
+
+ switch (cmd) {
+ case KDGKBSENT: {
+ /* size should have been a struct member */
+ ssize_t len = sizeof(user_kdgkb->kb_string);
+
+ char __free(kfree) *kbs = kmalloc(len, GFP_KERNEL);
+ if (!kbs)
+ return -ENOMEM;
+
+ scoped_guard(spinlock_irqsave, &func_buf_lock)
+ len = strscpy(kbs, func_table[kb_func] ? : "", len);
+
+ if (len < 0)
+ return -ENOSPC;
+
+ if (copy_to_user(user_kdgkb->kb_string, kbs, len + 1))
+ return -EFAULT;
+
+ return 0;
+ }
+ case KDSKBSENT:
+ if (!perm || !capable(CAP_SYS_TTY_CONFIG))
+ return -EPERM;
+
+ char __free(kfree) *kbs = strndup_user(user_kdgkb->kb_string,
+ sizeof(user_kdgkb->kb_string));
+ if (IS_ERR(kbs))
+ return PTR_ERR(kbs);
+
+ guard(spinlock_irqsave)(&func_buf_lock);
+ kbs = vt_kdskbsent(kbs, kb_func);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+ unsigned char ucval;
+
+ switch(cmd) {
+ /* the ioctls below read/set the flags usually shown in the leds */
+ /* don't use them - they will go away without warning */
+ case KDGKBLED:
+ scoped_guard(spinlock_irqsave, &kbd_event_lock)
+ ucval = kb->ledflagstate | (kb->default_ledflagstate << 4);
+ return put_user(ucval, (char __user *)arg);
+
+ case KDSKBLED:
+ if (!perm)
+ return -EPERM;
+ if (arg & ~0x77)
+ return -EINVAL;
+ scoped_guard(spinlock_irqsave, &led_lock) {
+ kb->ledflagstate = (arg & 7);
+ kb->default_ledflagstate = ((arg >> 4) & 7);
+ set_leds();
+ }
+ return 0;
+
+ /* the ioctls below only set the lights, not the functions */
+ /* for those, see KDGKBLED and KDSKBLED above */
+ case KDGETLED:
+ ucval = getledstate();
+ return put_user(ucval, (char __user *)arg);
+
+ case KDSETLED:
+ if (!perm)
+ return -EPERM;
+ setledstate(kb, arg);
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+int vt_do_kdgkbmode(unsigned int console)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+ /* This is a spot read so needs no locking */
+ switch (kb->kbdmode) {
+ case VC_RAW:
+ return K_RAW;
+ case VC_MEDIUMRAW:
+ return K_MEDIUMRAW;
+ case VC_UNICODE:
+ return K_UNICODE;
+ case VC_OFF:
+ return K_OFF;
+ default:
+ return K_XLATE;
+ }
+}
+
+/**
+ * vt_do_kdgkbmeta - report meta status
+ * @console: console to report
+ *
+ * Report the meta flag status of this console
+ */
+int vt_do_kdgkbmeta(unsigned int console)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+ /* Again a spot read so no locking */
+ return vc_kbd_mode(kb, VC_META) ? K_ESCPREFIX : K_METABIT;
+}
+
+/**
+ * vt_reset_unicode - reset the unicode status
+ * @console: console being reset
+ *
+ * Restore the unicode console state to its default
+ */
+void vt_reset_unicode(unsigned int console)
+{
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ kbd_table[console].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+}
+
+/**
+ * vt_get_shift_state - shift bit state
+ *
+ * Report the shift bits from the keyboard state. We have to export
+ * this to support some oddities in the vt layer.
+ */
+int vt_get_shift_state(void)
+{
+ /* Don't lock as this is a transient report */
+ return shift_state;
+}
+
+/**
+ * vt_reset_keyboard - reset keyboard state
+ * @console: console to reset
+ *
+ * Reset the keyboard bits for a console as part of a general console
+ * reset event
+ */
+void vt_reset_keyboard(unsigned int console)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ set_vc_kbd_mode(kb, VC_REPEAT);
+ clr_vc_kbd_mode(kb, VC_CKMODE);
+ clr_vc_kbd_mode(kb, VC_APPLIC);
+ clr_vc_kbd_mode(kb, VC_CRLF);
+ kb->lockstate = 0;
+ kb->slockstate = 0;
+ guard(spinlock)(&led_lock);
+ kb->ledmode = LED_SHOW_FLAGS;
+ kb->ledflagstate = kb->default_ledflagstate;
+ /* do not do set_leds here because this causes an endless tasklet loop
+ when the keyboard hasn't been initialized yet */
+}
+
+/**
+ * vt_get_kbd_mode_bit - read keyboard status bits
+ * @console: console to read from
+ * @bit: mode bit to read
+ *
+ * Report back a vt mode bit. We do this without locking so the
+ * caller must be sure that there are no synchronization needs
+ */
+
+int vt_get_kbd_mode_bit(unsigned int console, int bit)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+ return vc_kbd_mode(kb, bit);
+}
+
+/**
+ * vt_set_kbd_mode_bit - read keyboard status bits
+ * @console: console to read from
+ * @bit: mode bit to read
+ *
+ * Set a vt mode bit. We do this without locking so the
+ * caller must be sure that there are no synchronization needs
+ */
+
+void vt_set_kbd_mode_bit(unsigned int console, int bit)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ set_vc_kbd_mode(kb, bit);
+}
+
+/**
+ * vt_clr_kbd_mode_bit - read keyboard status bits
+ * @console: console to read from
+ * @bit: mode bit to read
+ *
+ * Report back a vt mode bit. We do this without locking so the
+ * caller must be sure that there are no synchronization needs
+ */
+
+void vt_clr_kbd_mode_bit(unsigned int console, int bit)
+{
+ struct kbd_struct *kb = &kbd_table[console];
+
+ guard(spinlock_irqsave)(&kbd_event_lock);
+ clr_vc_kbd_mode(kb, bit);
+}
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
new file mode 100644
index 000000000..13f4e48b4
--- /dev/null
+++ b/drivers/tty/vt/selection.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This module exports the functions:
+ *
+ * 'int set_selection_user(struct tiocl_selection __user *,
+ * struct tty_struct *)'
+ * 'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)'
+ * 'void clear_selection(void)'
+ * 'int paste_selection(struct tty_struct *)'
+ * 'int sel_loadlut(u32 __user *)'
+ *
+ * Now that /dev/vcs exists, most of this can disappear again.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <linux/uaccess.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/tiocl.h>
+#include <linux/console.h>
+#include <linux/tty_flip.h>
+
+#include <linux/sched/signal.h>
+
+/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
+#define is_space_on_vt(c) ((c) == ' ')
+
+/* FIXME: all this needs locking */
+static struct vc_selection {
+ struct mutex lock;
+ struct vc_data *cons; /* must not be deallocated */
+ char *buffer;
+ unsigned int buf_len;
+ volatile int start; /* cleared by clear_selection */
+ int end;
+} vc_sel = {
+ .lock = __MUTEX_INITIALIZER(vc_sel.lock),
+ .start = -1,
+};
+
+/* 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(vc_sel.cons, s, e-s+2, true);
+}
+
+/* use complementary color to show the pointer */
+static inline void highlight_pointer(const int where)
+{
+ complement_pos(vc_sel.cons, where);
+}
+
+static u32
+sel_pos(int n, bool unicode)
+{
+ if (unicode)
+ return screen_glyph_unicode(vc_sel.cons, n / 2);
+ return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n),
+ false);
+}
+
+/**
+ * clear_selection - remove current selection
+ *
+ * Remove the current selection highlight, if any from the console holding the
+ * selection.
+ *
+ * Locking: The caller must hold the console lock.
+ */
+void clear_selection(void)
+{
+ highlight_pointer(-1); /* hide the pointer */
+ if (vc_sel.start != -1) {
+ highlight(vc_sel.start, vc_sel.end);
+ vc_sel.start = -1;
+ }
+}
+EXPORT_SYMBOL_GPL(clear_selection);
+
+bool vc_is_sel(const struct vc_data *vc)
+{
+ return vc == vc_sel.cons;
+}
+
+/*
+ * User settable table: what characters are to be considered alphabetic?
+ * 128 bits. Locked by the console lock.
+ */
+static u32 inwordLut[]={
+ 0x00000000, /* control chars */
+ 0x03FFE000, /* digits and "-./" */
+ 0x87FFFFFE, /* uppercase and '_' */
+ 0x07FFFFFE, /* lowercase */
+};
+
+static inline int inword(const u32 c)
+{
+ return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
+}
+
+/**
+ * sel_loadlut() - load the LUT table
+ * @lut: user table
+ *
+ * Load the LUT table from user space. Make a temporary copy so a partial
+ * update doesn't make a mess.
+ *
+ * Locking: The console lock is acquired.
+ */
+int sel_loadlut(u32 __user *lut)
+{
+ u32 tmplut[ARRAY_SIZE(inwordLut)];
+
+ if (copy_from_user(tmplut, lut, sizeof(inwordLut)))
+ return -EFAULT;
+
+ guard(console_lock)();
+ memcpy(inwordLut, tmplut, sizeof(inwordLut));
+
+ return 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));
+}
+
+/* stores the char in UTF8 and returns the number of bytes used (1-4) */
+static int store_utf8(u32 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 if (c < 0x10000) {
+ /* 1110**** 10****** 10****** */
+ p[0] = 0xe0 | (c >> 12);
+ p[1] = 0x80 | ((c >> 6) & 0x3f);
+ p[2] = 0x80 | (c & 0x3f);
+ return 3;
+ } else if (c < 0x110000) {
+ /* 11110*** 10****** 10****** 10****** */
+ p[0] = 0xf0 | (c >> 18);
+ p[1] = 0x80 | ((c >> 12) & 0x3f);
+ p[2] = 0x80 | ((c >> 6) & 0x3f);
+ p[3] = 0x80 | (c & 0x3f);
+ return 4;
+ } else {
+ /* outside Unicode, replace with U+FFFD */
+ p[0] = 0xef;
+ p[1] = 0xbf;
+ p[2] = 0xbd;
+ return 3;
+ }
+}
+
+/**
+ * set_selection_user - set the current selection.
+ * @sel: user selection info
+ * @tty: the console tty
+ *
+ * Invoked by the ioctl handle for the vt layer.
+ *
+ * Locking: The entire selection process is managed under the console_lock.
+ * It's a lot under the lock but its hardly a performance path.
+ */
+int set_selection_user(const struct tiocl_selection __user *sel,
+ struct tty_struct *tty)
+{
+ struct tiocl_selection v;
+
+ if (copy_from_user(&v, sel, sizeof(*sel)))
+ return -EFAULT;
+
+ /*
+ * TIOCL_SELCLEAR and TIOCL_SELPOINTER are OK to use without
+ * CAP_SYS_ADMIN as they do not modify the selection.
+ */
+ switch (v.sel_mode) {
+ case TIOCL_SELCLEAR:
+ case TIOCL_SELPOINTER:
+ break;
+ default:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ }
+
+ return set_selection_kernel(&v, tty);
+}
+
+static int vc_selection_store_chars(struct vc_data *vc, bool unicode)
+{
+ char *bp, *obp;
+ unsigned int i;
+
+ /* Allocate a new buffer before freeing the old one ... */
+ /* chars can take up to 4 bytes with unicode */
+ bp = kmalloc_array((vc_sel.end - vc_sel.start) / 2 + 1, unicode ? 4 : 1,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!bp) {
+ printk(KERN_WARNING "selection: kmalloc() failed\n");
+ clear_selection();
+ return -ENOMEM;
+ }
+ kfree(vc_sel.buffer);
+ vc_sel.buffer = bp;
+
+ obp = bp;
+ for (i = vc_sel.start; i <= vc_sel.end; i += 2) {
+ u32 c = sel_pos(i, unicode);
+ if (unicode)
+ bp += store_utf8(c, bp);
+ else
+ *bp++ = c;
+ if (!is_space_on_vt(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;
+ }
+ }
+ vc_sel.buf_len = bp - vc_sel.buffer;
+
+ return 0;
+}
+
+static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps,
+ int pe)
+{
+ int new_sel_start, new_sel_end, spc;
+ bool unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE;
+
+ switch (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 = is_space_on_vt(sel_pos(ps, unicode));
+ for (new_sel_start = ps; ; ps -= 2) {
+ if ((spc && !is_space_on_vt(sel_pos(ps, unicode))) ||
+ (!spc && !inword(sel_pos(ps, unicode))))
+ break;
+ new_sel_start = ps;
+ if (!(ps % vc->vc_size_row))
+ break;
+ }
+
+ spc = is_space_on_vt(sel_pos(pe, unicode));
+ for (new_sel_end = pe; ; pe += 2) {
+ if ((spc && !is_space_on_vt(sel_pos(pe, unicode))) ||
+ (!spc && !inword(sel_pos(pe, unicode))))
+ break;
+ new_sel_end = pe;
+ if (!((pe + 2) % vc->vc_size_row))
+ break;
+ }
+ break;
+ case TIOCL_SELLINE: /* line-by-line selection */
+ new_sel_start = rounddown(ps, vc->vc_size_row);
+ new_sel_end = rounddown(pe, vc->vc_size_row) +
+ 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) &&
+ is_space_on_vt(sel_pos(new_sel_end, unicode))) {
+ for (pe = new_sel_end + 2; ; pe += 2)
+ if (!is_space_on_vt(sel_pos(pe, unicode)) ||
+ atedge(pe, vc->vc_size_row))
+ break;
+ if (is_space_on_vt(sel_pos(pe, unicode)))
+ new_sel_end = pe;
+ }
+ if (vc_sel.start == -1) /* no current selection */
+ highlight(new_sel_start, new_sel_end);
+ else if (new_sel_start == vc_sel.start)
+ {
+ if (new_sel_end == vc_sel.end) /* no action required */
+ return 0;
+ else if (new_sel_end > vc_sel.end) /* extend to right */
+ highlight(vc_sel.end + 2, new_sel_end);
+ else /* contract from right */
+ highlight(new_sel_end + 2, vc_sel.end);
+ }
+ else if (new_sel_end == vc_sel.end)
+ {
+ if (new_sel_start < vc_sel.start) /* extend to left */
+ highlight(new_sel_start, vc_sel.start - 2);
+ else /* contract from left */
+ highlight(vc_sel.start, new_sel_start - 2);
+ }
+ else /* some other case; start selection from scratch */
+ {
+ clear_selection();
+ highlight(new_sel_start, new_sel_end);
+ }
+ vc_sel.start = new_sel_start;
+ vc_sel.end = new_sel_end;
+
+ return vc_selection_store_chars(vc, unicode);
+}
+
+static int vc_selection(struct vc_data *vc, struct tiocl_selection *v,
+ struct tty_struct *tty)
+{
+ int ps, pe;
+
+ poke_blanked_console();
+
+ if (v->sel_mode == TIOCL_SELCLEAR) {
+ /* useful for screendump without selection highlights */
+ clear_selection();
+ return 0;
+ }
+
+ /* Historically 0 => max value */
+ v->xs = umin(v->xs - 1, vc->vc_cols - 1);
+ v->ys = umin(v->ys - 1, vc->vc_rows - 1);
+ v->xe = umin(v->xe - 1, vc->vc_cols - 1);
+ v->ye = umin(v->ye - 1, vc->vc_rows - 1);
+
+ if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
+ mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
+ v->ys);
+ return 0;
+ }
+
+ ps = v->ys * vc->vc_size_row + (v->xs << 1);
+ pe = v->ye * vc->vc_size_row + (v->xe << 1);
+ if (ps > pe) /* make vc_sel.start <= vc_sel.end */
+ swap(ps, pe);
+
+ if (vc_sel.cons != vc) {
+ clear_selection();
+ vc_sel.cons = vc;
+ }
+
+ return vc_do_selection(vc, v->sel_mode, ps, pe);
+}
+
+int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
+{
+ guard(mutex)(&vc_sel.lock);
+ guard(console_lock)();
+ return vc_selection(vc_cons[fg_console].d, v, tty);
+}
+EXPORT_SYMBOL_GPL(set_selection_kernel);
+
+/* Insert the contents of the selection buffer into the
+ * queue of the tty associated with the current console.
+ * Invoked by ioctl().
+ *
+ * Locking: called without locks. Calls the ldisc wrongly with
+ * unsafe methods,
+ */
+int paste_selection(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+ int pasted = 0;
+ size_t count;
+ struct tty_ldisc *ld;
+ DECLARE_WAITQUEUE(wait, current);
+ int ret = 0;
+
+ bool bp = vc->vc_bracketed_paste;
+ static const char bracketed_paste_start[] = "\033[200~";
+ static const char bracketed_paste_end[] = "\033[201~";
+ const char *bps = bp ? bracketed_paste_start : NULL;
+ const char *bpe = bp ? bracketed_paste_end : NULL;
+
+ scoped_guard(console_lock)
+ poke_blanked_console();
+
+ ld = tty_ldisc_ref_wait(tty);
+ if (!ld)
+ return -EIO; /* ldisc was hung up */
+ tty_buffer_lock_exclusive(&vc->port);
+
+ add_wait_queue(&vc->paste_wait, &wait);
+ mutex_lock(&vc_sel.lock);
+ while (vc_sel.buffer && (vc_sel.buf_len > pasted || bpe)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ if (tty_throttled(tty)) {
+ mutex_unlock(&vc_sel.lock);
+ schedule();
+ mutex_lock(&vc_sel.lock);
+ continue;
+ }
+ __set_current_state(TASK_RUNNING);
+
+ if (bps) {
+ bps += tty_ldisc_receive_buf(ld, bps, NULL, strlen(bps));
+ if (*bps != '\0')
+ continue;
+ bps = NULL;
+ }
+
+ count = vc_sel.buf_len - pasted;
+ if (count) {
+ pasted += tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted,
+ NULL, count);
+ if (vc_sel.buf_len > pasted)
+ continue;
+ }
+
+ if (bpe) {
+ bpe += tty_ldisc_receive_buf(ld, bpe, NULL, strlen(bpe));
+ if (*bpe == '\0')
+ bpe = NULL;
+ }
+ }
+ mutex_unlock(&vc_sel.lock);
+ remove_wait_queue(&vc->paste_wait, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ tty_buffer_unlock_exclusive(&vc->port);
+ tty_ldisc_deref(ld);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(paste_selection);
diff --git a/drivers/tty/vt/ucs.c b/drivers/tty/vt/ucs.c
new file mode 100644
index 000000000..fc41c0bb5
--- /dev/null
+++ b/drivers/tty/vt/ucs.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ucs.c - Universal Character Set processing
+ */
+
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/bsearch.h>
+#include <linux/consolemap.h>
+#include <linux/math.h>
+
+struct ucs_width16 {
+ u16 first;
+ u16 last;
+};
+
+struct ucs_width32 {
+ u32 first;
+ u32 last;
+};
+
+/*
+ * Width table encoding (consumed by ucs_width_table.h):
+ *
+ * Zero- and double-width ranges are merged into one sorted-by-`first` table
+ * per region (BMP / non-BMP). The BMP table stores plain (first, last)
+ * pairs; per-entry width lives in a packed bitmap *hosted by the non-BMP
+ * table*.
+ *
+ * That hosting is the whole point of the encoding. Non-BMP code points use
+ * only 20 bits, so each u32 has 12 spare high bits sitting around doing
+ * nothing — we'd rather use them than spend a separate parallel array for
+ * width and BMP-bitmap bits. So we move the cp value up by UCS_CP_SHIFT
+ * and stash metadata in the now-free low bits of `last`:
+ * - bit UCS_NONBMP_W2_FLAG_BIT: this entry's own width (0=zero, 1=double),
+ * - bits 0..UCS_NONBMP_BMP_BITS-1: a chunk of the BMP double-width
+ * bitmap. Bit `j` of the chunk in non-BMP entry `c` is set iff BMP
+ * entry (c * UCS_NONBMP_BMP_BITS + j) is double-width. The first
+ * ceil(N_BMP / UCS_NONBMP_BMP_BITS) non-BMP entries carry the bitmap;
+ * the rest leave these bits zero.
+ *
+ * Because the metadata bits sit strictly below the lowest cp-scale bit,
+ * the bsearch comparator does plain u32 comparison on the shifted key and
+ * stored values without masking — ordering between distinct code points is
+ * undisturbed.
+ */
+#define UCS_CP_SHIFT 12
+#define UCS_NONBMP_W2_FLAG_BIT 11
+#define UCS_NONBMP_W2_FLAG (1u << UCS_NONBMP_W2_FLAG_BIT)
+
+#define BMP_0WIDTH(first, last) first, last
+#define BMP_2WIDTH(first, last) first, last
+#define RANGE_0WIDTH(first, last) \
+ (u32)(first) << UCS_CP_SHIFT, (u32)(last) << UCS_CP_SHIFT
+#define RANGE_2WIDTH(first, last) \
+ (u32)(first) << UCS_CP_SHIFT, ((u32)(last) << UCS_CP_SHIFT) | UCS_NONBMP_W2_FLAG
+#define BMP_2W_BITS(b) (b)
+
+#include "ucs_width_table.h"
+
+static_assert(UCS_NONBMP_BMP_BITS <= UCS_NONBMP_W2_FLAG_BIT,
+ "BMP bitmap chunk would overlap the per-entry width flag");
+static_assert(UCS_NONBMP_W2_FLAG_BIT < UCS_CP_SHIFT,
+ "Metadata bits collide with the shifted cp value");
+static_assert(DIV_ROUND_UP(ARRAY_SIZE(ucs_bmp_ranges), UCS_NONBMP_BMP_BITS)
+ <= ARRAY_SIZE(ucs_nonbmp_ranges),
+ "Not enough non-BMP entries to host the BMP width bitmap");
+
+#define UCS_IS_BMP(cp) ((cp) <= 0xffff)
+
+static int width16_cmp(const void *key, const void *element)
+{
+ u16 cp = *(u16 *)key;
+ const struct ucs_width16 *entry = element;
+
+ if (cp < entry->first)
+ return -1;
+ if (cp > entry->last)
+ return 1;
+ return 0;
+}
+
+static int width32_cmp(const void *key, const void *element)
+{
+ u32 k = *(u32 *)key;
+ const struct ucs_width32 *entry = element;
+
+ if (k < entry->first)
+ return -1;
+ if (k > entry->last)
+ return 1;
+ return 0;
+}
+
+/**
+ * ucs_get_width() - Get the display width of a Unicode code point.
+ * @cp: Unicode code point (UCS-4)
+ *
+ * Return: 2 for double-width (East Asian Wide/Fullwidth, emoji, ...),
+ * 0 for zero-width (combining marks, format characters, ...),
+ * 1 for everything else (the common case).
+ */
+unsigned int ucs_get_width(u32 cp)
+{
+ const struct ucs_width16 *e16;
+ const struct ucs_width32 *e32;
+ unsigned int idx;
+ u32 k;
+
+ if (UCS_IS_BMP(cp)) {
+ u16 bmp = cp;
+
+ if (bmp < ucs_bmp_ranges[0].first ||
+ bmp > ucs_bmp_ranges[ARRAY_SIZE(ucs_bmp_ranges) - 1].last)
+ return 1;
+
+ e16 = __inline_bsearch(&bmp, ucs_bmp_ranges,
+ ARRAY_SIZE(ucs_bmp_ranges),
+ sizeof(*ucs_bmp_ranges), width16_cmp);
+ if (!e16)
+ return 1;
+
+ idx = e16 - ucs_bmp_ranges;
+ return (ucs_nonbmp_ranges[idx / UCS_NONBMP_BMP_BITS].last
+ >> (idx % UCS_NONBMP_BMP_BITS)) & 1 ? 2 : 0;
+ }
+
+ k = cp << UCS_CP_SHIFT;
+ if (k < ucs_nonbmp_ranges[0].first ||
+ k > ucs_nonbmp_ranges[ARRAY_SIZE(ucs_nonbmp_ranges) - 1].last)
+ return 1;
+
+ e32 = __inline_bsearch(&k, ucs_nonbmp_ranges,
+ ARRAY_SIZE(ucs_nonbmp_ranges),
+ sizeof(*ucs_nonbmp_ranges), width32_cmp);
+ if (!e32)
+ return 1;
+ return (e32->last & UCS_NONBMP_W2_FLAG) ? 2 : 0;
+}
+
+/*
+ * Structure for base with combining mark pairs and resulting recompositions.
+ * Using u16 to save space since all values are within BMP range.
+ */
+struct ucs_recomposition {
+ u16 base; /* base character */
+ u16 mark; /* combining mark */
+ u16 recomposed; /* corresponding recomposed character */
+};
+
+#include "ucs_recompose_table.h"
+
+struct compare_key {
+ u16 base;
+ u16 mark;
+};
+
+static int recomposition_cmp(const void *key, const void *element)
+{
+ const struct compare_key *search_key = key;
+ const struct ucs_recomposition *entry = element;
+
+ /* Compare base character first */
+ if (search_key->base < entry->base)
+ return -1;
+ if (search_key->base > entry->base)
+ return 1;
+
+ /* Base characters match, now compare combining character */
+ if (search_key->mark < entry->mark)
+ return -1;
+ if (search_key->mark > entry->mark)
+ return 1;
+
+ /* Both match */
+ return 0;
+}
+
+/**
+ * ucs_recompose() - Attempt to recompose two Unicode characters into a single character.
+ * @base: Base Unicode code point (UCS-4)
+ * @mark: Combining mark Unicode code point (UCS-4)
+ *
+ * Return: Recomposed Unicode code point, or 0 if no recomposition is possible
+ */
+u32 ucs_recompose(u32 base, u32 mark)
+{
+ /* Check if characters are within the range of our table */
+ if (base < UCS_RECOMPOSE_MIN_BASE || base > UCS_RECOMPOSE_MAX_BASE ||
+ mark < UCS_RECOMPOSE_MIN_MARK || mark > UCS_RECOMPOSE_MAX_MARK)
+ return 0;
+
+ struct compare_key key = { base, mark };
+ struct ucs_recomposition *result =
+ __inline_bsearch(&key, ucs_recomposition_table,
+ ARRAY_SIZE(ucs_recomposition_table),
+ sizeof(*ucs_recomposition_table),
+ recomposition_cmp);
+
+ return result ? result->recomposed : 0;
+}
+
+/*
+ * The fallback table structures implement a 2-level lookup.
+ */
+
+struct ucs_page_desc {
+ u8 page; /* Page index (high byte of code points) */
+ u8 count; /* Number of entries in this page */
+ u16 start; /* Start index in entries array */
+};
+
+struct ucs_page_entry {
+ u8 offset; /* Offset within page (0-255) */
+ u8 fallback; /* Fallback character or range start marker */
+};
+
+#include "ucs_fallback_table.h"
+
+static int ucs_page_desc_cmp(const void *key, const void *element)
+{
+ u8 page = *(u8 *)key;
+ const struct ucs_page_desc *entry = element;
+
+ if (page < entry->page)
+ return -1;
+ if (page > entry->page)
+ return 1;
+ return 0;
+}
+
+static int ucs_page_entry_cmp(const void *key, const void *element)
+{
+ u8 offset = *(u8 *)key;
+ const struct ucs_page_entry *entry = element;
+
+ if (offset < entry->offset)
+ return -1;
+ if (entry->fallback == UCS_PAGE_ENTRY_RANGE_MARKER) {
+ if (offset > entry[1].offset)
+ return 1;
+ } else {
+ if (offset > entry->offset)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * ucs_get_fallback() - Get a substitution for the provided Unicode character
+ * @cp: Unicode code point (UCS-4)
+ *
+ * Get a simpler fallback character for the provided Unicode character.
+ * This is used for terminal display when corresponding glyph is unavailable.
+ * The substitution may not be as good as the actual glyph for the original
+ * character but still way more helpful than a squared question mark.
+ *
+ * Return: Fallback Unicode code point, or 0 if none is available
+ */
+u32 ucs_get_fallback(u32 cp)
+{
+ const struct ucs_page_desc *page;
+ const struct ucs_page_entry *entry;
+ u8 page_idx = cp >> 8, offset = cp;
+
+ if (!UCS_IS_BMP(cp))
+ return 0;
+
+ /*
+ * Full-width to ASCII mapping (covering all printable ASCII 33-126)
+ * 0xFF01 (�) to 0xFF5E (~) -> ASCII 33 (!) to 126 (~)
+ * We process them programmatically to reduce the table size.
+ */
+ if (cp >= 0xFF01 && cp <= 0xFF5E)
+ return cp - 0xFF01 + 33;
+
+ page = __inline_bsearch(&page_idx, ucs_fallback_pages,
+ ARRAY_SIZE(ucs_fallback_pages),
+ sizeof(*ucs_fallback_pages),
+ ucs_page_desc_cmp);
+ if (!page)
+ return 0;
+
+ entry = __inline_bsearch(&offset, ucs_fallback_entries + page->start,
+ page->count, sizeof(*ucs_fallback_entries),
+ ucs_page_entry_cmp);
+ if (!entry)
+ return 0;
+
+ if (entry->fallback == UCS_PAGE_ENTRY_RANGE_MARKER)
+ entry++;
+ return entry->fallback;
+}
diff --git a/drivers/tty/vt/ucs_fallback_table.h_shipped b/drivers/tty/vt/ucs_fallback_table.h_shipped
new file mode 100644
index 000000000..2da5a8fe1
--- /dev/null
+++ b/drivers/tty/vt/ucs_fallback_table.h_shipped
@@ -0,0 +1,3346 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ucs_fallback_table.h - Unicode character fallback table
+ *
+ * Auto-generated by gen_ucs_fallback_table.py
+ *
+ * Unicode Version: 16.0.0
+ * Unidecode Version: 1.3.8
+ *
+ * This file contains optimized tables that map complex Unicode characters
+ * to simpler fallback characters for terminal display when corresponding
+ * glyphs are unavailable.
+ */
+
+static const struct ucs_page_desc ucs_fallback_pages[] = {
+ { 0x00, 62, 0 },
+ { 0x01, 218, 62 },
+ { 0x02, 196, 280 },
+ { 0x03, 96, 476 },
+ { 0x04, 113, 572 },
+ { 0x05, 100, 685 },
+ { 0x06, 119, 785 },
+ { 0x07, 91, 904 },
+ { 0x09, 99, 995 },
+ { 0x0A, 78, 1094 },
+ { 0x0B, 79, 1172 },
+ { 0x0C, 85, 1251 },
+ { 0x0D, 73, 1336 },
+ { 0x0E, 83, 1409 },
+ { 0x0F, 69, 1492 },
+ { 0x10, 93, 1561 },
+ { 0x11, 51, 1654 },
+ { 0x13, 22, 1705 },
+ { 0x14, 30, 1727 },
+ { 0x15, 17, 1757 },
+ { 0x16, 81, 1774 },
+ { 0x17, 47, 1855 },
+ { 0x18, 96, 1902 },
+ { 0x1D, 105, 1998 },
+ { 0x1E, 246, 2103 },
+ { 0x1F, 94, 2349 },
+ { 0x20, 107, 2443 },
+ { 0x21, 136, 2550 },
+ { 0x22, 34, 2686 },
+ { 0x23, 4, 2720 },
+ { 0x24, 72, 2724 },
+ { 0x25, 60, 2796 },
+ { 0x26, 6, 2856 },
+ { 0x27, 18, 2862 },
+ { 0x28, 64, 2880 },
+ { 0x29, 1, 2944 },
+ { 0x2C, 15, 2945 },
+ { 0x2E, 29, 2960 },
+ { 0x30, 53, 2989 },
+ { 0x31, 50, 3042 },
+ { 0x32, 5, 3092 },
+ { 0xA0, 4, 3097 },
+ { 0xC5, 2, 3101 },
+ { 0xC6, 2, 3103 },
+ { 0xC7, 1, 3105 },
+ { 0xFB, 35, 3106 },
+ { 0xFE, 37, 3141 },
+ { 0xFF, 50, 3178 },
+};
+
+/* Page entries array (referenced by page descriptors) */
+static const struct ucs_page_entry ucs_fallback_entries[] = {
+ /* Entries for page 0x00 */
+ { 0xA0, 0x20 }, /* NO-BREAK SPACE -> ' ' */
+ { 0xA1, 0x21 }, /* INVERTED EXCLAMATION MARK -> '!' */
+ { 0xA2, 0x63 }, /* CENT SIGN -> 'c' */
+ { 0xA3, 0x4C }, /* POUND SIGN -> 'L' */
+ { 0xA5, 0x59 }, /* YEN SIGN -> 'Y' */
+ { 0xA6, 0x7C }, /* BROKEN BAR -> '|' */
+ { 0xA7, 0x53 }, /* SECTION SIGN -> 'S' */
+ { 0xA8, 0x22 }, /* DIAERESIS -> '"' */
+ { 0xA9, 0x43 }, /* COPYRIGHT SIGN -> 'C' */
+ { 0xAA, 0x61 }, /* FEMININE ORDINAL INDICATOR -> 'a' */
+ { 0xAB, 0x3C }, /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -> '<' */
+ { 0xAC, 0x21 }, /* NOT SIGN -> '!' */
+ { 0xAE, 0x52 }, /* REGISTERED SIGN -> 'R' */
+ { 0xAF, 0x2D }, /* MACRON -> '-' */
+ { 0xB0, 0x6F }, /* DEGREE SIGN -> 'o' */
+ { 0xB2, 0x32 }, /* SUPERSCRIPT TWO -> '2' */
+ { 0xB3, 0x33 }, /* SUPERSCRIPT THREE -> '3' */
+ { 0xB4, 0x27 }, /* ACUTE ACCENT -> ''' */
+ { 0xB5, 0x75 }, /* MICRO SIGN -> 'u' */
+ { 0xB6, 0x50 }, /* PILCROW SIGN -> 'P' */
+ { 0xB7, 0x2A }, /* MIDDLE DOT -> '*' */
+ { 0xB8, 0x2C }, /* CEDILLA -> ',' */
+ { 0xB9, 0x31 }, /* SUPERSCRIPT ONE -> '1' */
+ { 0xBA, 0x6F }, /* MASCULINE ORDINAL INDICATOR -> 'o' */
+ { 0xBB, 0x3E }, /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -> '>' */
+ { 0xBF, 0x3F }, /* INVERTED QUESTION MARK -> '?' */
+ { 0xC0, 0x00 }, /* LATIN CAPITAL LETTER A WITH GRAVE -> ... */
+ { 0xC5, 0x41 }, /* LATIN CAPITAL LETTER A WITH RING ABOVE -> 'A' */
+ { 0xC6, 0x45 }, /* LATIN CAPITAL LETTER AE -> 'E' */
+ { 0xC7, 0x43 }, /* LATIN CAPITAL LETTER C WITH CEDILLA -> 'C' */
+ { 0xC8, 0x00 }, /* LATIN CAPITAL LETTER E WITH GRAVE -> ... */
+ { 0xCB, 0x45 }, /* LATIN CAPITAL LETTER E WITH DIAERESIS -> 'E' */
+ { 0xCC, 0x00 }, /* LATIN CAPITAL LETTER I WITH GRAVE -> ... */
+ { 0xCF, 0x49 }, /* LATIN CAPITAL LETTER I WITH DIAERESIS -> 'I' */
+ { 0xD0, 0x44 }, /* LATIN CAPITAL LETTER ETH -> 'D' */
+ { 0xD1, 0x4E }, /* LATIN CAPITAL LETTER N WITH TILDE -> 'N' */
+ { 0xD2, 0x00 }, /* LATIN CAPITAL LETTER O WITH GRAVE -> ... */
+ { 0xD6, 0x4F }, /* LATIN CAPITAL LETTER O WITH DIAERESIS -> 'O' */
+ { 0xD7, 0x78 }, /* MULTIPLICATION SIGN -> 'x' */
+ { 0xD8, 0x4F }, /* LATIN CAPITAL LETTER O WITH STROKE -> 'O' */
+ { 0xD9, 0x00 }, /* LATIN CAPITAL LETTER U WITH GRAVE -> ... */
+ { 0xDC, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS -> 'U' */
+ { 0xDD, 0x59 }, /* LATIN CAPITAL LETTER Y WITH ACUTE -> 'Y' */
+ { 0xDF, 0x73 }, /* LATIN SMALL LETTER SHARP S -> 's' */
+ { 0xE0, 0x00 }, /* LATIN SMALL LETTER A WITH GRAVE -> ... */
+ { 0xE5, 0x61 }, /* LATIN SMALL LETTER A WITH RING ABOVE -> 'a' */
+ { 0xE6, 0x65 }, /* LATIN SMALL LETTER AE -> 'e' */
+ { 0xE7, 0x63 }, /* LATIN SMALL LETTER C WITH CEDILLA -> 'c' */
+ { 0xE8, 0x00 }, /* LATIN SMALL LETTER E WITH GRAVE -> ... */
+ { 0xEB, 0x65 }, /* LATIN SMALL LETTER E WITH DIAERESIS -> 'e' */
+ { 0xEC, 0x00 }, /* LATIN SMALL LETTER I WITH GRAVE -> ... */
+ { 0xEF, 0x69 }, /* LATIN SMALL LETTER I WITH DIAERESIS -> 'i' */
+ { 0xF0, 0x64 }, /* LATIN SMALL LETTER ETH -> 'd' */
+ { 0xF1, 0x6E }, /* LATIN SMALL LETTER N WITH TILDE -> 'n' */
+ { 0xF2, 0x00 }, /* LATIN SMALL LETTER O WITH GRAVE -> ... */
+ { 0xF6, 0x6F }, /* LATIN SMALL LETTER O WITH DIAERESIS -> 'o' */
+ { 0xF7, 0x2F }, /* DIVISION SIGN -> '/' */
+ { 0xF8, 0x6F }, /* LATIN SMALL LETTER O WITH STROKE -> 'o' */
+ { 0xF9, 0x00 }, /* LATIN SMALL LETTER U WITH GRAVE -> ... */
+ { 0xFC, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS -> 'u' */
+ { 0xFD, 0x79 }, /* LATIN SMALL LETTER Y WITH ACUTE -> 'y' */
+ { 0xFF, 0x79 }, /* LATIN SMALL LETTER Y WITH DIAERESIS -> 'y' */
+ /* Entries for page 0x01 */
+ { 0x00, 0x41 }, /* LATIN CAPITAL LETTER A WITH MACRON -> 'A' */
+ { 0x01, 0x61 }, /* LATIN SMALL LETTER A WITH MACRON -> 'a' */
+ { 0x02, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE -> 'A' */
+ { 0x03, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE -> 'a' */
+ { 0x04, 0x41 }, /* LATIN CAPITAL LETTER A WITH OGONEK -> 'A' */
+ { 0x05, 0x61 }, /* LATIN SMALL LETTER A WITH OGONEK -> 'a' */
+ { 0x06, 0x43 }, /* LATIN CAPITAL LETTER C WITH ACUTE -> 'C' */
+ { 0x07, 0x63 }, /* LATIN SMALL LETTER C WITH ACUTE -> 'c' */
+ { 0x08, 0x43 }, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX -> 'C' */
+ { 0x09, 0x63 }, /* LATIN SMALL LETTER C WITH CIRCUMFLEX -> 'c' */
+ { 0x0A, 0x43 }, /* LATIN CAPITAL LETTER C WITH DOT ABOVE -> 'C' */
+ { 0x0B, 0x63 }, /* LATIN SMALL LETTER C WITH DOT ABOVE -> 'c' */
+ { 0x0C, 0x43 }, /* LATIN CAPITAL LETTER C WITH CARON -> 'C' */
+ { 0x0D, 0x63 }, /* LATIN SMALL LETTER C WITH CARON -> 'c' */
+ { 0x0E, 0x44 }, /* LATIN CAPITAL LETTER D WITH CARON -> 'D' */
+ { 0x0F, 0x64 }, /* LATIN SMALL LETTER D WITH CARON -> 'd' */
+ { 0x10, 0x44 }, /* LATIN CAPITAL LETTER D WITH STROKE -> 'D' */
+ { 0x11, 0x64 }, /* LATIN SMALL LETTER D WITH STROKE -> 'd' */
+ { 0x12, 0x45 }, /* LATIN CAPITAL LETTER E WITH MACRON -> 'E' */
+ { 0x13, 0x65 }, /* LATIN SMALL LETTER E WITH MACRON -> 'e' */
+ { 0x14, 0x45 }, /* LATIN CAPITAL LETTER E WITH BREVE -> 'E' */
+ { 0x15, 0x65 }, /* LATIN SMALL LETTER E WITH BREVE -> 'e' */
+ { 0x16, 0x45 }, /* LATIN CAPITAL LETTER E WITH DOT ABOVE -> 'E' */
+ { 0x17, 0x65 }, /* LATIN SMALL LETTER E WITH DOT ABOVE -> 'e' */
+ { 0x18, 0x45 }, /* LATIN CAPITAL LETTER E WITH OGONEK -> 'E' */
+ { 0x19, 0x65 }, /* LATIN SMALL LETTER E WITH OGONEK -> 'e' */
+ { 0x1A, 0x45 }, /* LATIN CAPITAL LETTER E WITH CARON -> 'E' */
+ { 0x1B, 0x65 }, /* LATIN SMALL LETTER E WITH CARON -> 'e' */
+ { 0x1C, 0x47 }, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX -> 'G' */
+ { 0x1D, 0x67 }, /* LATIN SMALL LETTER G WITH CIRCUMFLEX -> 'g' */
+ { 0x1E, 0x47 }, /* LATIN CAPITAL LETTER G WITH BREVE -> 'G' */
+ { 0x1F, 0x67 }, /* LATIN SMALL LETTER G WITH BREVE -> 'g' */
+ { 0x20, 0x47 }, /* LATIN CAPITAL LETTER G WITH DOT ABOVE -> 'G' */
+ { 0x21, 0x67 }, /* LATIN SMALL LETTER G WITH DOT ABOVE -> 'g' */
+ { 0x22, 0x47 }, /* LATIN CAPITAL LETTER G WITH CEDILLA -> 'G' */
+ { 0x23, 0x67 }, /* LATIN SMALL LETTER G WITH CEDILLA -> 'g' */
+ { 0x24, 0x48 }, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX -> 'H' */
+ { 0x25, 0x68 }, /* LATIN SMALL LETTER H WITH CIRCUMFLEX -> 'h' */
+ { 0x26, 0x48 }, /* LATIN CAPITAL LETTER H WITH STROKE -> 'H' */
+ { 0x27, 0x68 }, /* LATIN SMALL LETTER H WITH STROKE -> 'h' */
+ { 0x28, 0x49 }, /* LATIN CAPITAL LETTER I WITH TILDE -> 'I' */
+ { 0x29, 0x69 }, /* LATIN SMALL LETTER I WITH TILDE -> 'i' */
+ { 0x2A, 0x49 }, /* LATIN CAPITAL LETTER I WITH MACRON -> 'I' */
+ { 0x2B, 0x69 }, /* LATIN SMALL LETTER I WITH MACRON -> 'i' */
+ { 0x2C, 0x49 }, /* LATIN CAPITAL LETTER I WITH BREVE -> 'I' */
+ { 0x2D, 0x69 }, /* LATIN SMALL LETTER I WITH BREVE -> 'i' */
+ { 0x2E, 0x49 }, /* LATIN CAPITAL LETTER I WITH OGONEK -> 'I' */
+ { 0x2F, 0x69 }, /* LATIN SMALL LETTER I WITH OGONEK -> 'i' */
+ { 0x30, 0x49 }, /* LATIN CAPITAL LETTER I WITH DOT ABOVE -> 'I' */
+ { 0x31, 0x69 }, /* LATIN SMALL LETTER DOTLESS I -> 'i' */
+ { 0x34, 0x4A }, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX -> 'J' */
+ { 0x35, 0x6A }, /* LATIN SMALL LETTER J WITH CIRCUMFLEX -> 'j' */
+ { 0x36, 0x4B }, /* LATIN CAPITAL LETTER K WITH CEDILLA -> 'K' */
+ { 0x37, 0x6B }, /* LATIN SMALL LETTER K WITH CEDILLA -> 'k' */
+ { 0x38, 0x6B }, /* LATIN SMALL LETTER KRA -> 'k' */
+ { 0x39, 0x4C }, /* LATIN CAPITAL LETTER L WITH ACUTE -> 'L' */
+ { 0x3A, 0x6C }, /* LATIN SMALL LETTER L WITH ACUTE -> 'l' */
+ { 0x3B, 0x4C }, /* LATIN CAPITAL LETTER L WITH CEDILLA -> 'L' */
+ { 0x3C, 0x6C }, /* LATIN SMALL LETTER L WITH CEDILLA -> 'l' */
+ { 0x3D, 0x4C }, /* LATIN CAPITAL LETTER L WITH CARON -> 'L' */
+ { 0x3E, 0x6C }, /* LATIN SMALL LETTER L WITH CARON -> 'l' */
+ { 0x3F, 0x4C }, /* LATIN CAPITAL LETTER L WITH MIDDLE DOT -> 'L' */
+ { 0x40, 0x6C }, /* LATIN SMALL LETTER L WITH MIDDLE DOT -> 'l' */
+ { 0x41, 0x4C }, /* LATIN CAPITAL LETTER L WITH STROKE -> 'L' */
+ { 0x42, 0x6C }, /* LATIN SMALL LETTER L WITH STROKE -> 'l' */
+ { 0x43, 0x4E }, /* LATIN CAPITAL LETTER N WITH ACUTE -> 'N' */
+ { 0x44, 0x6E }, /* LATIN SMALL LETTER N WITH ACUTE -> 'n' */
+ { 0x45, 0x4E }, /* LATIN CAPITAL LETTER N WITH CEDILLA -> 'N' */
+ { 0x46, 0x6E }, /* LATIN SMALL LETTER N WITH CEDILLA -> 'n' */
+ { 0x47, 0x4E }, /* LATIN CAPITAL LETTER N WITH CARON -> 'N' */
+ { 0x48, 0x6E }, /* LATIN SMALL LETTER N WITH CARON -> 'n' */
+ { 0x4C, 0x4F }, /* LATIN CAPITAL LETTER O WITH MACRON -> 'O' */
+ { 0x4D, 0x6F }, /* LATIN SMALL LETTER O WITH MACRON -> 'o' */
+ { 0x4E, 0x4F }, /* LATIN CAPITAL LETTER O WITH BREVE -> 'O' */
+ { 0x4F, 0x6F }, /* LATIN SMALL LETTER O WITH BREVE -> 'o' */
+ { 0x50, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE -> 'O' */
+ { 0x51, 0x6F }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE -> 'o' */
+ { 0x52, 0x45 }, /* LATIN CAPITAL LIGATURE OE -> 'E' */
+ { 0x53, 0x65 }, /* LATIN SMALL LIGATURE OE -> 'e' */
+ { 0x54, 0x52 }, /* LATIN CAPITAL LETTER R WITH ACUTE -> 'R' */
+ { 0x55, 0x72 }, /* LATIN SMALL LETTER R WITH ACUTE -> 'r' */
+ { 0x56, 0x52 }, /* LATIN CAPITAL LETTER R WITH CEDILLA -> 'R' */
+ { 0x57, 0x72 }, /* LATIN SMALL LETTER R WITH CEDILLA -> 'r' */
+ { 0x58, 0x52 }, /* LATIN CAPITAL LETTER R WITH CARON -> 'R' */
+ { 0x59, 0x72 }, /* LATIN SMALL LETTER R WITH CARON -> 'r' */
+ { 0x5A, 0x53 }, /* LATIN CAPITAL LETTER S WITH ACUTE -> 'S' */
+ { 0x5B, 0x73 }, /* LATIN SMALL LETTER S WITH ACUTE -> 's' */
+ { 0x5C, 0x53 }, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX -> 'S' */
+ { 0x5D, 0x73 }, /* LATIN SMALL LETTER S WITH CIRCUMFLEX -> 's' */
+ { 0x5E, 0x53 }, /* LATIN CAPITAL LETTER S WITH CEDILLA -> 'S' */
+ { 0x5F, 0x73 }, /* LATIN SMALL LETTER S WITH CEDILLA -> 's' */
+ { 0x60, 0x53 }, /* LATIN CAPITAL LETTER S WITH CARON -> 'S' */
+ { 0x61, 0x73 }, /* LATIN SMALL LETTER S WITH CARON -> 's' */
+ { 0x62, 0x54 }, /* LATIN CAPITAL LETTER T WITH CEDILLA -> 'T' */
+ { 0x63, 0x74 }, /* LATIN SMALL LETTER T WITH CEDILLA -> 't' */
+ { 0x64, 0x54 }, /* LATIN CAPITAL LETTER T WITH CARON -> 'T' */
+ { 0x65, 0x74 }, /* LATIN SMALL LETTER T WITH CARON -> 't' */
+ { 0x66, 0x54 }, /* LATIN CAPITAL LETTER T WITH STROKE -> 'T' */
+ { 0x67, 0x74 }, /* LATIN SMALL LETTER T WITH STROKE -> 't' */
+ { 0x68, 0x55 }, /* LATIN CAPITAL LETTER U WITH TILDE -> 'U' */
+ { 0x69, 0x75 }, /* LATIN SMALL LETTER U WITH TILDE -> 'u' */
+ { 0x6A, 0x55 }, /* LATIN CAPITAL LETTER U WITH MACRON -> 'U' */
+ { 0x6B, 0x75 }, /* LATIN SMALL LETTER U WITH MACRON -> 'u' */
+ { 0x6C, 0x55 }, /* LATIN CAPITAL LETTER U WITH BREVE -> 'U' */
+ { 0x6D, 0x75 }, /* LATIN SMALL LETTER U WITH BREVE -> 'u' */
+ { 0x6E, 0x55 }, /* LATIN CAPITAL LETTER U WITH RING ABOVE -> 'U' */
+ { 0x6F, 0x75 }, /* LATIN SMALL LETTER U WITH RING ABOVE -> 'u' */
+ { 0x70, 0x55 }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE -> 'U' */
+ { 0x71, 0x75 }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE -> 'u' */
+ { 0x72, 0x55 }, /* LATIN CAPITAL LETTER U WITH OGONEK -> 'U' */
+ { 0x73, 0x75 }, /* LATIN SMALL LETTER U WITH OGONEK -> 'u' */
+ { 0x74, 0x57 }, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX -> 'W' */
+ { 0x75, 0x77 }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX -> 'w' */
+ { 0x76, 0x59 }, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX -> 'Y' */
+ { 0x77, 0x79 }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX -> 'y' */
+ { 0x78, 0x59 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS -> 'Y' */
+ { 0x79, 0x5A }, /* LATIN CAPITAL LETTER Z WITH ACUTE -> 'Z' */
+ { 0x7A, 0x7A }, /* LATIN SMALL LETTER Z WITH ACUTE -> 'z' */
+ { 0x7B, 0x5A }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE -> 'Z' */
+ { 0x7C, 0x7A }, /* LATIN SMALL LETTER Z WITH DOT ABOVE -> 'z' */
+ { 0x7D, 0x5A }, /* LATIN CAPITAL LETTER Z WITH CARON -> 'Z' */
+ { 0x7E, 0x7A }, /* LATIN SMALL LETTER Z WITH CARON -> 'z' */
+ { 0x7F, 0x73 }, /* LATIN SMALL LETTER LONG S -> 's' */
+ { 0x80, 0x62 }, /* LATIN SMALL LETTER B WITH STROKE -> 'b' */
+ { 0x81, 0x42 }, /* LATIN CAPITAL LETTER B WITH HOOK -> 'B' */
+ { 0x82, 0x42 }, /* LATIN CAPITAL LETTER B WITH TOPBAR -> 'B' */
+ { 0x83, 0x62 }, /* LATIN SMALL LETTER B WITH TOPBAR -> 'b' */
+ { 0x84, 0x36 }, /* LATIN CAPITAL LETTER TONE SIX -> '6' */
+ { 0x85, 0x36 }, /* LATIN SMALL LETTER TONE SIX -> '6' */
+ { 0x86, 0x4F }, /* LATIN CAPITAL LETTER OPEN O -> 'O' */
+ { 0x87, 0x43 }, /* LATIN CAPITAL LETTER C WITH HOOK -> 'C' */
+ { 0x88, 0x63 }, /* LATIN SMALL LETTER C WITH HOOK -> 'c' */
+ { 0x89, 0x00 }, /* LATIN CAPITAL LETTER AFRICAN D -> ... */
+ { 0x8B, 0x44 }, /* LATIN CAPITAL LETTER D WITH TOPBAR -> 'D' */
+ { 0x8C, 0x64 }, /* LATIN SMALL LETTER D WITH TOPBAR -> 'd' */
+ { 0x8D, 0x64 }, /* LATIN SMALL LETTER TURNED DELTA -> 'd' */
+ { 0x8E, 0x33 }, /* LATIN CAPITAL LETTER REVERSED E -> '3' */
+ { 0x8F, 0x40 }, /* LATIN CAPITAL LETTER SCHWA -> '@' */
+ { 0x90, 0x45 }, /* LATIN CAPITAL LETTER OPEN E -> 'E' */
+ { 0x91, 0x46 }, /* LATIN CAPITAL LETTER F WITH HOOK -> 'F' */
+ { 0x92, 0x66 }, /* LATIN SMALL LETTER F WITH HOOK -> 'f' */
+ { 0x93, 0x47 }, /* LATIN CAPITAL LETTER G WITH HOOK -> 'G' */
+ { 0x94, 0x47 }, /* LATIN CAPITAL LETTER GAMMA -> 'G' */
+ { 0x96, 0x49 }, /* LATIN CAPITAL LETTER IOTA -> 'I' */
+ { 0x97, 0x49 }, /* LATIN CAPITAL LETTER I WITH STROKE -> 'I' */
+ { 0x98, 0x4B }, /* LATIN CAPITAL LETTER K WITH HOOK -> 'K' */
+ { 0x99, 0x6B }, /* LATIN SMALL LETTER K WITH HOOK -> 'k' */
+ { 0x9A, 0x6C }, /* LATIN SMALL LETTER L WITH BAR -> 'l' */
+ { 0x9B, 0x6C }, /* LATIN SMALL LETTER LAMBDA WITH STROKE -> 'l' */
+ { 0x9C, 0x57 }, /* LATIN CAPITAL LETTER TURNED M -> 'W' */
+ { 0x9D, 0x4E }, /* LATIN CAPITAL LETTER N WITH LEFT HOOK -> 'N' */
+ { 0x9E, 0x6E }, /* LATIN SMALL LETTER N WITH LONG RIGHT LEG -> 'n' */
+ { 0x9F, 0x4F }, /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE -> 'O' */
+ { 0xA0, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN -> 'O' */
+ { 0xA1, 0x6F }, /* LATIN SMALL LETTER O WITH HORN -> 'o' */
+ { 0xA4, 0x50 }, /* LATIN CAPITAL LETTER P WITH HOOK -> 'P' */
+ { 0xA5, 0x70 }, /* LATIN SMALL LETTER P WITH HOOK -> 'p' */
+ { 0xA7, 0x32 }, /* LATIN CAPITAL LETTER TONE TWO -> '2' */
+ { 0xA8, 0x32 }, /* LATIN SMALL LETTER TONE TWO -> '2' */
+ { 0xAB, 0x74 }, /* LATIN SMALL LETTER T WITH PALATAL HOOK -> 't' */
+ { 0xAC, 0x54 }, /* LATIN CAPITAL LETTER T WITH HOOK -> 'T' */
+ { 0xAD, 0x74 }, /* LATIN SMALL LETTER T WITH HOOK -> 't' */
+ { 0xAE, 0x54 }, /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK -> 'T' */
+ { 0xAF, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN -> 'U' */
+ { 0xB0, 0x75 }, /* LATIN SMALL LETTER U WITH HORN -> 'u' */
+ { 0xB1, 0x59 }, /* LATIN CAPITAL LETTER UPSILON -> 'Y' */
+ { 0xB2, 0x56 }, /* LATIN CAPITAL LETTER V WITH HOOK -> 'V' */
+ { 0xB3, 0x59 }, /* LATIN CAPITAL LETTER Y WITH HOOK -> 'Y' */
+ { 0xB4, 0x79 }, /* LATIN SMALL LETTER Y WITH HOOK -> 'y' */
+ { 0xB5, 0x5A }, /* LATIN CAPITAL LETTER Z WITH STROKE -> 'Z' */
+ { 0xB6, 0x7A }, /* LATIN SMALL LETTER Z WITH STROKE -> 'z' */
+ { 0xBB, 0x32 }, /* LATIN LETTER TWO WITH STROKE -> '2' */
+ { 0xBC, 0x35 }, /* LATIN CAPITAL LETTER TONE FIVE -> '5' */
+ { 0xBD, 0x35 }, /* LATIN SMALL LETTER TONE FIVE -> '5' */
+ { 0xBF, 0x77 }, /* LATIN LETTER WYNN -> 'w' */
+ { 0xC0, 0x7C }, /* LATIN LETTER DENTAL CLICK -> '|' */
+ { 0xC3, 0x21 }, /* LATIN LETTER RETROFLEX CLICK -> '!' */
+ { 0xCD, 0x41 }, /* LATIN CAPITAL LETTER A WITH CARON -> 'A' */
+ { 0xCE, 0x61 }, /* LATIN SMALL LETTER A WITH CARON -> 'a' */
+ { 0xCF, 0x49 }, /* LATIN CAPITAL LETTER I WITH CARON -> 'I' */
+ { 0xD0, 0x69 }, /* LATIN SMALL LETTER I WITH CARON -> 'i' */
+ { 0xD1, 0x4F }, /* LATIN CAPITAL LETTER O WITH CARON -> 'O' */
+ { 0xD2, 0x6F }, /* LATIN SMALL LETTER O WITH CARON -> 'o' */
+ { 0xD3, 0x55 }, /* LATIN CAPITAL LETTER U WITH CARON -> 'U' */
+ { 0xD4, 0x75 }, /* LATIN SMALL LETTER U WITH CARON -> 'u' */
+ { 0xD5, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON -> 'U' */
+ { 0xD6, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND MACRON -> 'u' */
+ { 0xD7, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE -> 'U' */
+ { 0xD8, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE -> 'u' */
+ { 0xD9, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON -> 'U' */
+ { 0xDA, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND CARON -> 'u' */
+ { 0xDB, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE -> 'U' */
+ { 0xDC, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE -> 'u' */
+ { 0xDD, 0x40 }, /* LATIN SMALL LETTER TURNED E -> '@' */
+ { 0xDE, 0x41 }, /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON -> 'A' */
+ { 0xDF, 0x61 }, /* LATIN SMALL LETTER A WITH DIAERESIS AND MACRON -> 'a' */
+ { 0xE0, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON -> 'A' */
+ { 0xE1, 0x61 }, /* LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON -> 'a' */
+ { 0xE4, 0x47 }, /* LATIN CAPITAL LETTER G WITH STROKE -> 'G' */
+ { 0xE5, 0x67 }, /* LATIN SMALL LETTER G WITH STROKE -> 'g' */
+ { 0xE6, 0x47 }, /* LATIN CAPITAL LETTER G WITH CARON -> 'G' */
+ { 0xE7, 0x67 }, /* LATIN SMALL LETTER G WITH CARON -> 'g' */
+ { 0xE8, 0x4B }, /* LATIN CAPITAL LETTER K WITH CARON -> 'K' */
+ { 0xE9, 0x6B }, /* LATIN SMALL LETTER K WITH CARON -> 'k' */
+ { 0xEA, 0x4F }, /* LATIN CAPITAL LETTER O WITH OGONEK -> 'O' */
+ { 0xEB, 0x6F }, /* LATIN SMALL LETTER O WITH OGONEK -> 'o' */
+ { 0xEC, 0x4F }, /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON -> 'O' */
+ { 0xED, 0x6F }, /* LATIN SMALL LETTER O WITH OGONEK AND MACRON -> 'o' */
+ { 0xF0, 0x6A }, /* LATIN SMALL LETTER J WITH CARON -> 'j' */
+ { 0xF4, 0x47 }, /* LATIN CAPITAL LETTER G WITH ACUTE -> 'G' */
+ { 0xF5, 0x67 }, /* LATIN SMALL LETTER G WITH ACUTE -> 'g' */
+ { 0xF7, 0x57 }, /* LATIN CAPITAL LETTER WYNN -> 'W' */
+ { 0xF8, 0x4E }, /* LATIN CAPITAL LETTER N WITH GRAVE -> 'N' */
+ { 0xF9, 0x6E }, /* LATIN SMALL LETTER N WITH GRAVE -> 'n' */
+ { 0xFA, 0x41 }, /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE -> 'A' */
+ { 0xFB, 0x61 }, /* LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE -> 'a' */
+ { 0xFE, 0x4F }, /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE -> 'O' */
+ { 0xFF, 0x6F }, /* LATIN SMALL LETTER O WITH STROKE AND ACUTE -> 'o' */
+ /* Entries for page 0x02 */
+ { 0x00, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE -> 'A' */
+ { 0x01, 0x61 }, /* LATIN SMALL LETTER A WITH DOUBLE GRAVE -> 'a' */
+ { 0x02, 0x41 }, /* LATIN CAPITAL LETTER A WITH INVERTED BREVE -> 'A' */
+ { 0x03, 0x61 }, /* LATIN SMALL LETTER A WITH INVERTED BREVE -> 'a' */
+ { 0x04, 0x45 }, /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE -> 'E' */
+ { 0x05, 0x65 }, /* LATIN SMALL LETTER E WITH DOUBLE GRAVE -> 'e' */
+ { 0x06, 0x45 }, /* LATIN CAPITAL LETTER E WITH INVERTED BREVE -> 'E' */
+ { 0x07, 0x65 }, /* LATIN SMALL LETTER E WITH INVERTED BREVE -> 'e' */
+ { 0x08, 0x49 }, /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE -> 'I' */
+ { 0x09, 0x69 }, /* LATIN SMALL LETTER I WITH DOUBLE GRAVE -> 'i' */
+ { 0x0A, 0x49 }, /* LATIN CAPITAL LETTER I WITH INVERTED BREVE -> 'I' */
+ { 0x0B, 0x69 }, /* LATIN SMALL LETTER I WITH INVERTED BREVE -> 'i' */
+ { 0x0C, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE -> 'O' */
+ { 0x0D, 0x6F }, /* LATIN SMALL LETTER O WITH DOUBLE GRAVE -> 'o' */
+ { 0x0E, 0x4F }, /* LATIN CAPITAL LETTER O WITH INVERTED BREVE -> 'O' */
+ { 0x0F, 0x6F }, /* LATIN SMALL LETTER O WITH INVERTED BREVE -> 'o' */
+ { 0x10, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE -> 'R' */
+ { 0x11, 0x72 }, /* LATIN SMALL LETTER R WITH DOUBLE GRAVE -> 'r' */
+ { 0x12, 0x52 }, /* LATIN CAPITAL LETTER R WITH INVERTED BREVE -> 'R' */
+ { 0x13, 0x72 }, /* LATIN SMALL LETTER R WITH INVERTED BREVE -> 'r' */
+ { 0x14, 0x55 }, /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE -> 'U' */
+ { 0x15, 0x75 }, /* LATIN SMALL LETTER U WITH DOUBLE GRAVE -> 'u' */
+ { 0x16, 0x55 }, /* LATIN CAPITAL LETTER U WITH INVERTED BREVE -> 'U' */
+ { 0x17, 0x75 }, /* LATIN SMALL LETTER U WITH INVERTED BREVE -> 'u' */
+ { 0x18, 0x53 }, /* LATIN CAPITAL LETTER S WITH COMMA BELOW -> 'S' */
+ { 0x19, 0x73 }, /* LATIN SMALL LETTER S WITH COMMA BELOW -> 's' */
+ { 0x1A, 0x54 }, /* LATIN CAPITAL LETTER T WITH COMMA BELOW -> 'T' */
+ { 0x1B, 0x74 }, /* LATIN SMALL LETTER T WITH COMMA BELOW -> 't' */
+ { 0x1C, 0x59 }, /* LATIN CAPITAL LETTER YOGH -> 'Y' */
+ { 0x1D, 0x79 }, /* LATIN SMALL LETTER YOGH -> 'y' */
+ { 0x1E, 0x48 }, /* LATIN CAPITAL LETTER H WITH CARON -> 'H' */
+ { 0x1F, 0x68 }, /* LATIN SMALL LETTER H WITH CARON -> 'h' */
+ { 0x20, 0x4E }, /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG -> 'N' */
+ { 0x21, 0x64 }, /* LATIN SMALL LETTER D WITH CURL -> 'd' */
+ { 0x24, 0x5A }, /* LATIN CAPITAL LETTER Z WITH HOOK -> 'Z' */
+ { 0x25, 0x7A }, /* LATIN SMALL LETTER Z WITH HOOK -> 'z' */
+ { 0x26, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOT ABOVE -> 'A' */
+ { 0x27, 0x61 }, /* LATIN SMALL LETTER A WITH DOT ABOVE -> 'a' */
+ { 0x28, 0x45 }, /* LATIN CAPITAL LETTER E WITH CEDILLA -> 'E' */
+ { 0x29, 0x65 }, /* LATIN SMALL LETTER E WITH CEDILLA -> 'e' */
+ { 0x2A, 0x4F }, /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON -> 'O' */
+ { 0x2B, 0x6F }, /* LATIN SMALL LETTER O WITH DIAERESIS AND MACRON -> 'o' */
+ { 0x2C, 0x4F }, /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON -> 'O' */
+ { 0x2D, 0x6F }, /* LATIN SMALL LETTER O WITH TILDE AND MACRON -> 'o' */
+ { 0x2E, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOT ABOVE -> 'O' */
+ { 0x2F, 0x6F }, /* LATIN SMALL LETTER O WITH DOT ABOVE -> 'o' */
+ { 0x30, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON -> 'O' */
+ { 0x31, 0x6F }, /* LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON -> 'o' */
+ { 0x32, 0x59 }, /* LATIN CAPITAL LETTER Y WITH MACRON -> 'Y' */
+ { 0x33, 0x79 }, /* LATIN SMALL LETTER Y WITH MACRON -> 'y' */
+ { 0x34, 0x6C }, /* LATIN SMALL LETTER L WITH CURL -> 'l' */
+ { 0x35, 0x6E }, /* LATIN SMALL LETTER N WITH CURL -> 'n' */
+ { 0x36, 0x74 }, /* LATIN SMALL LETTER T WITH CURL -> 't' */
+ { 0x37, 0x6A }, /* LATIN SMALL LETTER DOTLESS J -> 'j' */
+ { 0x3A, 0x41 }, /* LATIN CAPITAL LETTER A WITH STROKE -> 'A' */
+ { 0x3B, 0x43 }, /* LATIN CAPITAL LETTER C WITH STROKE -> 'C' */
+ { 0x3C, 0x63 }, /* LATIN SMALL LETTER C WITH STROKE -> 'c' */
+ { 0x3D, 0x4C }, /* LATIN CAPITAL LETTER L WITH BAR -> 'L' */
+ { 0x3E, 0x54 }, /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE -> 'T' */
+ { 0x3F, 0x73 }, /* LATIN SMALL LETTER S WITH SWASH TAIL -> 's' */
+ { 0x40, 0x7A }, /* LATIN SMALL LETTER Z WITH SWASH TAIL -> 'z' */
+ { 0x43, 0x42 }, /* LATIN CAPITAL LETTER B WITH STROKE -> 'B' */
+ { 0x44, 0x55 }, /* LATIN CAPITAL LETTER U BAR -> 'U' */
+ { 0x45, 0x5E }, /* LATIN CAPITAL LETTER TURNED V -> '^' */
+ { 0x46, 0x45 }, /* LATIN CAPITAL LETTER E WITH STROKE -> 'E' */
+ { 0x47, 0x65 }, /* LATIN SMALL LETTER E WITH STROKE -> 'e' */
+ { 0x48, 0x4A }, /* LATIN CAPITAL LETTER J WITH STROKE -> 'J' */
+ { 0x49, 0x6A }, /* LATIN SMALL LETTER J WITH STROKE -> 'j' */
+ { 0x4A, 0x71 }, /* LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL -> 'q' */
+ { 0x4B, 0x71 }, /* LATIN SMALL LETTER Q WITH HOOK TAIL -> 'q' */
+ { 0x4C, 0x52 }, /* LATIN CAPITAL LETTER R WITH STROKE -> 'R' */
+ { 0x4D, 0x72 }, /* LATIN SMALL LETTER R WITH STROKE -> 'r' */
+ { 0x4E, 0x59 }, /* LATIN CAPITAL LETTER Y WITH STROKE -> 'Y' */
+ { 0x4F, 0x79 }, /* LATIN SMALL LETTER Y WITH STROKE -> 'y' */
+ { 0x50, 0x00 }, /* LATIN SMALL LETTER TURNED A -> ... */
+ { 0x52, 0x61 }, /* LATIN SMALL LETTER TURNED ALPHA -> 'a' */
+ { 0x53, 0x62 }, /* LATIN SMALL LETTER B WITH HOOK -> 'b' */
+ { 0x54, 0x6F }, /* LATIN SMALL LETTER OPEN O -> 'o' */
+ { 0x55, 0x63 }, /* LATIN SMALL LETTER C WITH CURL -> 'c' */
+ { 0x56, 0x64 }, /* LATIN SMALL LETTER D WITH TAIL -> 'd' */
+ { 0x57, 0x64 }, /* LATIN SMALL LETTER D WITH HOOK -> 'd' */
+ { 0x58, 0x65 }, /* LATIN SMALL LETTER REVERSED E -> 'e' */
+ { 0x59, 0x40 }, /* LATIN SMALL LETTER SCHWA -> '@' */
+ { 0x5A, 0x40 }, /* LATIN SMALL LETTER SCHWA WITH HOOK -> '@' */
+ { 0x5B, 0x00 }, /* LATIN SMALL LETTER OPEN E -> ... */
+ { 0x5E, 0x65 }, /* LATIN SMALL LETTER CLOSED REVERSED OPEN E -> 'e' */
+ { 0x5F, 0x6A }, /* LATIN SMALL LETTER DOTLESS J WITH STROKE -> 'j' */
+ { 0x60, 0x00 }, /* LATIN SMALL LETTER G WITH HOOK -> ... */
+ { 0x63, 0x67 }, /* LATIN SMALL LETTER GAMMA -> 'g' */
+ { 0x64, 0x75 }, /* LATIN SMALL LETTER RAMS HORN -> 'u' */
+ { 0x65, 0x59 }, /* LATIN SMALL LETTER TURNED H -> 'Y' */
+ { 0x66, 0x68 }, /* LATIN SMALL LETTER H WITH HOOK -> 'h' */
+ { 0x67, 0x68 }, /* LATIN SMALL LETTER HENG WITH HOOK -> 'h' */
+ { 0x68, 0x69 }, /* LATIN SMALL LETTER I WITH STROKE -> 'i' */
+ { 0x69, 0x69 }, /* LATIN SMALL LETTER IOTA -> 'i' */
+ { 0x6A, 0x49 }, /* LATIN LETTER SMALL CAPITAL I -> 'I' */
+ { 0x6B, 0x00 }, /* LATIN SMALL LETTER L WITH MIDDLE TILDE -> ... */
+ { 0x6D, 0x6C }, /* LATIN SMALL LETTER L WITH RETROFLEX HOOK -> 'l' */
+ { 0x6F, 0x57 }, /* LATIN SMALL LETTER TURNED M -> 'W' */
+ { 0x70, 0x57 }, /* LATIN SMALL LETTER TURNED M WITH LONG LEG -> 'W' */
+ { 0x71, 0x6D }, /* LATIN SMALL LETTER M WITH HOOK -> 'm' */
+ { 0x72, 0x00 }, /* LATIN SMALL LETTER N WITH LEFT HOOK -> ... */
+ { 0x74, 0x6E }, /* LATIN LETTER SMALL CAPITAL N -> 'n' */
+ { 0x75, 0x6F }, /* LATIN SMALL LETTER BARRED O -> 'o' */
+ { 0x77, 0x4F }, /* LATIN SMALL LETTER CLOSED OMEGA -> 'O' */
+ { 0x78, 0x46 }, /* LATIN SMALL LETTER PHI -> 'F' */
+ { 0x79, 0x00 }, /* LATIN SMALL LETTER TURNED R -> ... */
+ { 0x7F, 0x72 }, /* LATIN SMALL LETTER REVERSED R WITH FISHHOOK -> 'r' */
+ { 0x80, 0x52 }, /* LATIN LETTER SMALL CAPITAL R -> 'R' */
+ { 0x81, 0x52 }, /* LATIN LETTER SMALL CAPITAL INVERTED R -> 'R' */
+ { 0x82, 0x73 }, /* LATIN SMALL LETTER S WITH HOOK -> 's' */
+ { 0x83, 0x53 }, /* LATIN SMALL LETTER ESH -> 'S' */
+ { 0x84, 0x6A }, /* LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK -> 'j' */
+ { 0x85, 0x53 }, /* LATIN SMALL LETTER SQUAT REVERSED ESH -> 'S' */
+ { 0x86, 0x53 }, /* LATIN SMALL LETTER ESH WITH CURL -> 'S' */
+ { 0x87, 0x74 }, /* LATIN SMALL LETTER TURNED T -> 't' */
+ { 0x88, 0x74 }, /* LATIN SMALL LETTER T WITH RETROFLEX HOOK -> 't' */
+ { 0x89, 0x75 }, /* LATIN SMALL LETTER U BAR -> 'u' */
+ { 0x8A, 0x55 }, /* LATIN SMALL LETTER UPSILON -> 'U' */
+ { 0x8B, 0x76 }, /* LATIN SMALL LETTER V WITH HOOK -> 'v' */
+ { 0x8C, 0x5E }, /* LATIN SMALL LETTER TURNED V -> '^' */
+ { 0x8D, 0x77 }, /* LATIN SMALL LETTER TURNED W -> 'w' */
+ { 0x8E, 0x79 }, /* LATIN SMALL LETTER TURNED Y -> 'y' */
+ { 0x8F, 0x59 }, /* LATIN LETTER SMALL CAPITAL Y -> 'Y' */
+ { 0x90, 0x7A }, /* LATIN SMALL LETTER Z WITH RETROFLEX HOOK -> 'z' */
+ { 0x91, 0x7A }, /* LATIN SMALL LETTER Z WITH CURL -> 'z' */
+ { 0x92, 0x5A }, /* LATIN SMALL LETTER EZH -> 'Z' */
+ { 0x93, 0x5A }, /* LATIN SMALL LETTER EZH WITH CURL -> 'Z' */
+ { 0x94, 0x00 }, /* LATIN LETTER GLOTTAL STOP -> ... */
+ { 0x96, 0x3F }, /* LATIN LETTER INVERTED GLOTTAL STOP -> '?' */
+ { 0x97, 0x43 }, /* LATIN LETTER STRETCHED C -> 'C' */
+ { 0x98, 0x40 }, /* LATIN LETTER BILABIAL CLICK -> '@' */
+ { 0x99, 0x42 }, /* LATIN LETTER SMALL CAPITAL B -> 'B' */
+ { 0x9A, 0x45 }, /* LATIN SMALL LETTER CLOSED OPEN E -> 'E' */
+ { 0x9B, 0x47 }, /* LATIN LETTER SMALL CAPITAL G WITH HOOK -> 'G' */
+ { 0x9C, 0x48 }, /* LATIN LETTER SMALL CAPITAL H -> 'H' */
+ { 0x9D, 0x6A }, /* LATIN SMALL LETTER J WITH CROSSED-TAIL -> 'j' */
+ { 0x9E, 0x6B }, /* LATIN SMALL LETTER TURNED K -> 'k' */
+ { 0x9F, 0x4C }, /* LATIN LETTER SMALL CAPITAL L -> 'L' */
+ { 0xA0, 0x71 }, /* LATIN SMALL LETTER Q WITH HOOK -> 'q' */
+ { 0xA1, 0x3F }, /* LATIN LETTER GLOTTAL STOP WITH STROKE -> '?' */
+ { 0xA2, 0x3F }, /* LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE -> '?' */
+ { 0xAE, 0x00 }, /* LATIN SMALL LETTER TURNED H WITH FISHHOOK -> ... */
+ { 0xB1, 0x68 }, /* MODIFIER LETTER SMALL H WITH HOOK -> 'h' */
+ { 0xB2, 0x6A }, /* MODIFIER LETTER SMALL J -> 'j' */
+ { 0xB3, 0x00 }, /* MODIFIER LETTER SMALL R -> ... */
+ { 0xB6, 0x72 }, /* MODIFIER LETTER SMALL CAPITAL INVERTED R -> 'r' */
+ { 0xB7, 0x77 }, /* MODIFIER LETTER SMALL W -> 'w' */
+ { 0xB8, 0x79 }, /* MODIFIER LETTER SMALL Y -> 'y' */
+ { 0xB9, 0x27 }, /* MODIFIER LETTER PRIME -> ''' */
+ { 0xBA, 0x22 }, /* MODIFIER LETTER DOUBLE PRIME -> '"' */
+ { 0xBB, 0x60 }, /* MODIFIER LETTER TURNED COMMA -> '`' */
+ { 0xBC, 0x27 }, /* MODIFIER LETTER APOSTROPHE -> ''' */
+ { 0xBD, 0x60 }, /* MODIFIER LETTER REVERSED COMMA -> '`' */
+ { 0xBE, 0x60 }, /* MODIFIER LETTER RIGHT HALF RING -> '`' */
+ { 0xBF, 0x27 }, /* MODIFIER LETTER LEFT HALF RING -> ''' */
+ { 0xC0, 0x3F }, /* MODIFIER LETTER GLOTTAL STOP -> '?' */
+ { 0xC1, 0x3F }, /* MODIFIER LETTER REVERSED GLOTTAL STOP -> '?' */
+ { 0xC2, 0x3C }, /* MODIFIER LETTER LEFT ARROWHEAD -> '<' */
+ { 0xC3, 0x3E }, /* MODIFIER LETTER RIGHT ARROWHEAD -> '>' */
+ { 0xC4, 0x5E }, /* MODIFIER LETTER UP ARROWHEAD -> '^' */
+ { 0xC5, 0x56 }, /* MODIFIER LETTER DOWN ARROWHEAD -> 'V' */
+ { 0xC6, 0x5E }, /* MODIFIER LETTER CIRCUMFLEX ACCENT -> '^' */
+ { 0xC7, 0x56 }, /* CARON -> 'V' */
+ { 0xC8, 0x27 }, /* MODIFIER LETTER VERTICAL LINE -> ''' */
+ { 0xC9, 0x2D }, /* MODIFIER LETTER MACRON -> '-' */
+ { 0xCA, 0x2F }, /* MODIFIER LETTER ACUTE ACCENT -> '/' */
+ { 0xCB, 0x5C }, /* MODIFIER LETTER GRAVE ACCENT -> '\' */
+ { 0xCC, 0x2C }, /* MODIFIER LETTER LOW VERTICAL LINE -> ',' */
+ { 0xCD, 0x5F }, /* MODIFIER LETTER LOW MACRON -> '_' */
+ { 0xCE, 0x5C }, /* MODIFIER LETTER LOW GRAVE ACCENT -> '\' */
+ { 0xCF, 0x2F }, /* MODIFIER LETTER LOW ACUTE ACCENT -> '/' */
+ { 0xD0, 0x3A }, /* MODIFIER LETTER TRIANGULAR COLON -> ':' */
+ { 0xD1, 0x2E }, /* MODIFIER LETTER HALF TRIANGULAR COLON -> '.' */
+ { 0xD2, 0x60 }, /* MODIFIER LETTER CENTRED RIGHT HALF RING -> '`' */
+ { 0xD3, 0x27 }, /* MODIFIER LETTER CENTRED LEFT HALF RING -> ''' */
+ { 0xD4, 0x5E }, /* MODIFIER LETTER UP TACK -> '^' */
+ { 0xD5, 0x56 }, /* MODIFIER LETTER DOWN TACK -> 'V' */
+ { 0xD6, 0x2B }, /* MODIFIER LETTER PLUS SIGN -> '+' */
+ { 0xD7, 0x2D }, /* MODIFIER LETTER MINUS SIGN -> '-' */
+ { 0xD8, 0x56 }, /* BREVE -> 'V' */
+ { 0xD9, 0x2E }, /* DOT ABOVE -> '.' */
+ { 0xDA, 0x40 }, /* RING ABOVE -> '@' */
+ { 0xDB, 0x2C }, /* OGONEK -> ',' */
+ { 0xDC, 0x7E }, /* SMALL TILDE -> '~' */
+ { 0xDD, 0x22 }, /* DOUBLE ACUTE ACCENT -> '"' */
+ { 0xDE, 0x52 }, /* MODIFIER LETTER RHOTIC HOOK -> 'R' */
+ { 0xDF, 0x58 }, /* MODIFIER LETTER CROSS ACCENT -> 'X' */
+ { 0xE0, 0x47 }, /* MODIFIER LETTER SMALL GAMMA -> 'G' */
+ { 0xE1, 0x6C }, /* MODIFIER LETTER SMALL L -> 'l' */
+ { 0xE2, 0x73 }, /* MODIFIER LETTER SMALL S -> 's' */
+ { 0xE3, 0x78 }, /* MODIFIER LETTER SMALL X -> 'x' */
+ { 0xE4, 0x3F }, /* MODIFIER LETTER SMALL REVERSED GLOTTAL STOP -> '?' */
+ { 0xEC, 0x56 }, /* MODIFIER LETTER VOICING -> 'V' */
+ { 0xED, 0x3D }, /* MODIFIER LETTER UNASPIRATED -> '=' */
+ { 0xEE, 0x22 }, /* MODIFIER LETTER DOUBLE APOSTROPHE -> '"' */
+ /* Entries for page 0x03 */
+ { 0x63, 0x61 }, /* COMBINING LATIN SMALL LETTER A -> 'a' */
+ { 0x64, 0x65 }, /* COMBINING LATIN SMALL LETTER E -> 'e' */
+ { 0x65, 0x69 }, /* COMBINING LATIN SMALL LETTER I -> 'i' */
+ { 0x66, 0x6F }, /* COMBINING LATIN SMALL LETTER O -> 'o' */
+ { 0x67, 0x75 }, /* COMBINING LATIN SMALL LETTER U -> 'u' */
+ { 0x68, 0x63 }, /* COMBINING LATIN SMALL LETTER C -> 'c' */
+ { 0x69, 0x64 }, /* COMBINING LATIN SMALL LETTER D -> 'd' */
+ { 0x6A, 0x68 }, /* COMBINING LATIN SMALL LETTER H -> 'h' */
+ { 0x6B, 0x6D }, /* COMBINING LATIN SMALL LETTER M -> 'm' */
+ { 0x6C, 0x72 }, /* COMBINING LATIN SMALL LETTER R -> 'r' */
+ { 0x6D, 0x74 }, /* COMBINING LATIN SMALL LETTER T -> 't' */
+ { 0x6E, 0x76 }, /* COMBINING LATIN SMALL LETTER V -> 'v' */
+ { 0x6F, 0x78 }, /* COMBINING LATIN SMALL LETTER X -> 'x' */
+ { 0x74, 0x27 }, /* GREEK NUMERAL SIGN -> ''' */
+ { 0x75, 0x2C }, /* GREEK LOWER NUMERAL SIGN -> ',' */
+ { 0x7E, 0x3F }, /* GREEK QUESTION MARK -> '?' */
+ { 0x86, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH TONOS -> 'A' */
+ { 0x87, 0x3B }, /* GREEK ANO TELEIA -> ';' */
+ { 0x88, 0x45 }, /* GREEK CAPITAL LETTER EPSILON WITH TONOS -> 'E' */
+ { 0x89, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH TONOS -> 'E' */
+ { 0x8A, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH TONOS -> 'I' */
+ { 0x8C, 0x4F }, /* GREEK CAPITAL LETTER OMICRON WITH TONOS -> 'O' */
+ { 0x8E, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH TONOS -> 'U' */
+ { 0x8F, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH TONOS -> 'O' */
+ { 0x90, 0x49 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -> 'I' */
+ { 0x91, 0x41 }, /* GREEK CAPITAL LETTER ALPHA -> 'A' */
+ { 0x92, 0x42 }, /* GREEK CAPITAL LETTER BETA -> 'B' */
+ { 0x93, 0x47 }, /* GREEK CAPITAL LETTER GAMMA -> 'G' */
+ { 0x94, 0x44 }, /* GREEK CAPITAL LETTER DELTA -> 'D' */
+ { 0x95, 0x45 }, /* GREEK CAPITAL LETTER EPSILON -> 'E' */
+ { 0x96, 0x5A }, /* GREEK CAPITAL LETTER ZETA -> 'Z' */
+ { 0x97, 0x45 }, /* GREEK CAPITAL LETTER ETA -> 'E' */
+ { 0x99, 0x49 }, /* GREEK CAPITAL LETTER IOTA -> 'I' */
+ { 0x9A, 0x4B }, /* GREEK CAPITAL LETTER KAPPA -> 'K' */
+ { 0x9B, 0x4C }, /* GREEK CAPITAL LETTER LAMDA -> 'L' */
+ { 0x9C, 0x4D }, /* GREEK CAPITAL LETTER MU -> 'M' */
+ { 0x9D, 0x4E }, /* GREEK CAPITAL LETTER NU -> 'N' */
+ { 0x9F, 0x4F }, /* GREEK CAPITAL LETTER OMICRON -> 'O' */
+ { 0xA0, 0x50 }, /* GREEK CAPITAL LETTER PI -> 'P' */
+ { 0xA1, 0x52 }, /* GREEK CAPITAL LETTER RHO -> 'R' */
+ { 0xA3, 0x53 }, /* GREEK CAPITAL LETTER SIGMA -> 'S' */
+ { 0xA4, 0x54 }, /* GREEK CAPITAL LETTER TAU -> 'T' */
+ { 0xA5, 0x55 }, /* GREEK CAPITAL LETTER UPSILON -> 'U' */
+ { 0xA9, 0x4F }, /* GREEK CAPITAL LETTER OMEGA -> 'O' */
+ { 0xAA, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA -> 'I' */
+ { 0xAB, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA -> 'U' */
+ { 0xAC, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH TONOS -> 'a' */
+ { 0xAD, 0x65 }, /* GREEK SMALL LETTER EPSILON WITH TONOS -> 'e' */
+ { 0xAE, 0x65 }, /* GREEK SMALL LETTER ETA WITH TONOS -> 'e' */
+ { 0xAF, 0x69 }, /* GREEK SMALL LETTER IOTA WITH TONOS -> 'i' */
+ { 0xB0, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -> 'u' */
+ { 0xB1, 0x61 }, /* GREEK SMALL LETTER ALPHA -> 'a' */
+ { 0xB2, 0x62 }, /* GREEK SMALL LETTER BETA -> 'b' */
+ { 0xB3, 0x67 }, /* GREEK SMALL LETTER GAMMA -> 'g' */
+ { 0xB4, 0x64 }, /* GREEK SMALL LETTER DELTA -> 'd' */
+ { 0xB5, 0x65 }, /* GREEK SMALL LETTER EPSILON -> 'e' */
+ { 0xB6, 0x7A }, /* GREEK SMALL LETTER ZETA -> 'z' */
+ { 0xB7, 0x65 }, /* GREEK SMALL LETTER ETA -> 'e' */
+ { 0xB9, 0x69 }, /* GREEK SMALL LETTER IOTA -> 'i' */
+ { 0xBA, 0x6B }, /* GREEK SMALL LETTER KAPPA -> 'k' */
+ { 0xBB, 0x6C }, /* GREEK SMALL LETTER LAMDA -> 'l' */
+ { 0xBC, 0x6D }, /* GREEK SMALL LETTER MU -> 'm' */
+ { 0xBD, 0x6E }, /* GREEK SMALL LETTER NU -> 'n' */
+ { 0xBE, 0x78 }, /* GREEK SMALL LETTER XI -> 'x' */
+ { 0xBF, 0x6F }, /* GREEK SMALL LETTER OMICRON -> 'o' */
+ { 0xC0, 0x70 }, /* GREEK SMALL LETTER PI -> 'p' */
+ { 0xC1, 0x72 }, /* GREEK SMALL LETTER RHO -> 'r' */
+ { 0xC2, 0x73 }, /* GREEK SMALL LETTER FINAL SIGMA -> 's' */
+ { 0xC3, 0x73 }, /* GREEK SMALL LETTER SIGMA -> 's' */
+ { 0xC4, 0x74 }, /* GREEK SMALL LETTER TAU -> 't' */
+ { 0xC5, 0x75 }, /* GREEK SMALL LETTER UPSILON -> 'u' */
+ { 0xC9, 0x6F }, /* GREEK SMALL LETTER OMEGA -> 'o' */
+ { 0xCA, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA -> 'i' */
+ { 0xCB, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA -> 'u' */
+ { 0xCC, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH TONOS -> 'o' */
+ { 0xCD, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH TONOS -> 'u' */
+ { 0xCE, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH TONOS -> 'o' */
+ { 0xD0, 0x62 }, /* GREEK BETA SYMBOL -> 'b' */
+ { 0xD2, 0x00 }, /* GREEK UPSILON WITH HOOK SYMBOL -> ... */
+ { 0xD4, 0x55 }, /* GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL -> 'U' */
+ { 0xD6, 0x70 }, /* GREEK PI SYMBOL -> 'p' */
+ { 0xD7, 0x26 }, /* GREEK KAI SYMBOL -> '&' */
+ { 0xDC, 0x57 }, /* GREEK LETTER DIGAMMA -> 'W' */
+ { 0xDD, 0x77 }, /* GREEK SMALL LETTER DIGAMMA -> 'w' */
+ { 0xDE, 0x51 }, /* GREEK LETTER KOPPA -> 'Q' */
+ { 0xDF, 0x71 }, /* GREEK SMALL LETTER KOPPA -> 'q' */
+ { 0xE4, 0x46 }, /* COPTIC CAPITAL LETTER FEI -> 'F' */
+ { 0xE5, 0x66 }, /* COPTIC SMALL LETTER FEI -> 'f' */
+ { 0xE8, 0x48 }, /* COPTIC CAPITAL LETTER HORI -> 'H' */
+ { 0xE9, 0x68 }, /* COPTIC SMALL LETTER HORI -> 'h' */
+ { 0xEA, 0x47 }, /* COPTIC CAPITAL LETTER GANGIA -> 'G' */
+ { 0xEB, 0x67 }, /* COPTIC SMALL LETTER GANGIA -> 'g' */
+ { 0xF0, 0x6B }, /* GREEK KAPPA SYMBOL -> 'k' */
+ { 0xF1, 0x72 }, /* GREEK RHO SYMBOL -> 'r' */
+ { 0xF2, 0x63 }, /* GREEK LUNATE SIGMA SYMBOL -> 'c' */
+ { 0xF3, 0x6A }, /* GREEK LETTER YOT -> 'j' */
+ /* Entries for page 0x04 */
+ { 0x06, 0x49 }, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I -> 'I' */
+ { 0x08, 0x4A }, /* CYRILLIC CAPITAL LETTER JE -> 'J' */
+ { 0x0D, 0x49 }, /* CYRILLIC CAPITAL LETTER I WITH GRAVE -> 'I' */
+ { 0x0E, 0x55 }, /* CYRILLIC CAPITAL LETTER SHORT U -> 'U' */
+ { 0x10, 0x41 }, /* CYRILLIC CAPITAL LETTER A -> 'A' */
+ { 0x11, 0x42 }, /* CYRILLIC CAPITAL LETTER BE -> 'B' */
+ { 0x12, 0x56 }, /* CYRILLIC CAPITAL LETTER VE -> 'V' */
+ { 0x13, 0x47 }, /* CYRILLIC CAPITAL LETTER GHE -> 'G' */
+ { 0x14, 0x44 }, /* CYRILLIC CAPITAL LETTER DE -> 'D' */
+ { 0x15, 0x45 }, /* CYRILLIC CAPITAL LETTER IE -> 'E' */
+ { 0x17, 0x5A }, /* CYRILLIC CAPITAL LETTER ZE -> 'Z' */
+ { 0x18, 0x49 }, /* CYRILLIC CAPITAL LETTER I -> 'I' */
+ { 0x19, 0x49 }, /* CYRILLIC CAPITAL LETTER SHORT I -> 'I' */
+ { 0x1A, 0x4B }, /* CYRILLIC CAPITAL LETTER KA -> 'K' */
+ { 0x1B, 0x4C }, /* CYRILLIC CAPITAL LETTER EL -> 'L' */
+ { 0x1C, 0x4D }, /* CYRILLIC CAPITAL LETTER EM -> 'M' */
+ { 0x1D, 0x4E }, /* CYRILLIC CAPITAL LETTER EN -> 'N' */
+ { 0x1E, 0x4F }, /* CYRILLIC CAPITAL LETTER O -> 'O' */
+ { 0x1F, 0x50 }, /* CYRILLIC CAPITAL LETTER PE -> 'P' */
+ { 0x20, 0x52 }, /* CYRILLIC CAPITAL LETTER ER -> 'R' */
+ { 0x21, 0x53 }, /* CYRILLIC CAPITAL LETTER ES -> 'S' */
+ { 0x22, 0x54 }, /* CYRILLIC CAPITAL LETTER TE -> 'T' */
+ { 0x23, 0x55 }, /* CYRILLIC CAPITAL LETTER U -> 'U' */
+ { 0x24, 0x46 }, /* CYRILLIC CAPITAL LETTER EF -> 'F' */
+ { 0x2A, 0x27 }, /* CYRILLIC CAPITAL LETTER HARD SIGN -> ''' */
+ { 0x2B, 0x59 }, /* CYRILLIC CAPITAL LETTER YERU -> 'Y' */
+ { 0x2C, 0x27 }, /* CYRILLIC CAPITAL LETTER SOFT SIGN -> ''' */
+ { 0x2D, 0x45 }, /* CYRILLIC CAPITAL LETTER E -> 'E' */
+ { 0x30, 0x61 }, /* CYRILLIC SMALL LETTER A -> 'a' */
+ { 0x31, 0x62 }, /* CYRILLIC SMALL LETTER BE -> 'b' */
+ { 0x32, 0x76 }, /* CYRILLIC SMALL LETTER VE -> 'v' */
+ { 0x33, 0x67 }, /* CYRILLIC SMALL LETTER GHE -> 'g' */
+ { 0x34, 0x64 }, /* CYRILLIC SMALL LETTER DE -> 'd' */
+ { 0x35, 0x65 }, /* CYRILLIC SMALL LETTER IE -> 'e' */
+ { 0x37, 0x7A }, /* CYRILLIC SMALL LETTER ZE -> 'z' */
+ { 0x38, 0x69 }, /* CYRILLIC SMALL LETTER I -> 'i' */
+ { 0x39, 0x69 }, /* CYRILLIC SMALL LETTER SHORT I -> 'i' */
+ { 0x3A, 0x6B }, /* CYRILLIC SMALL LETTER KA -> 'k' */
+ { 0x3B, 0x6C }, /* CYRILLIC SMALL LETTER EL -> 'l' */
+ { 0x3C, 0x6D }, /* CYRILLIC SMALL LETTER EM -> 'm' */
+ { 0x3D, 0x6E }, /* CYRILLIC SMALL LETTER EN -> 'n' */
+ { 0x3E, 0x6F }, /* CYRILLIC SMALL LETTER O -> 'o' */
+ { 0x3F, 0x70 }, /* CYRILLIC SMALL LETTER PE -> 'p' */
+ { 0x40, 0x72 }, /* CYRILLIC SMALL LETTER ER -> 'r' */
+ { 0x41, 0x73 }, /* CYRILLIC SMALL LETTER ES -> 's' */
+ { 0x42, 0x74 }, /* CYRILLIC SMALL LETTER TE -> 't' */
+ { 0x43, 0x75 }, /* CYRILLIC SMALL LETTER U -> 'u' */
+ { 0x44, 0x66 }, /* CYRILLIC SMALL LETTER EF -> 'f' */
+ { 0x4A, 0x27 }, /* CYRILLIC SMALL LETTER HARD SIGN -> ''' */
+ { 0x4B, 0x79 }, /* CYRILLIC SMALL LETTER YERU -> 'y' */
+ { 0x4C, 0x27 }, /* CYRILLIC SMALL LETTER SOFT SIGN -> ''' */
+ { 0x4D, 0x65 }, /* CYRILLIC SMALL LETTER E -> 'e' */
+ { 0x56, 0x69 }, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I -> 'i' */
+ { 0x58, 0x6A }, /* CYRILLIC SMALL LETTER JE -> 'j' */
+ { 0x5D, 0x69 }, /* CYRILLIC SMALL LETTER I WITH GRAVE -> 'i' */
+ { 0x5E, 0x75 }, /* CYRILLIC SMALL LETTER SHORT U -> 'u' */
+ { 0x60, 0x4F }, /* CYRILLIC CAPITAL LETTER OMEGA -> 'O' */
+ { 0x61, 0x6F }, /* CYRILLIC SMALL LETTER OMEGA -> 'o' */
+ { 0x62, 0x45 }, /* CYRILLIC CAPITAL LETTER YAT -> 'E' */
+ { 0x63, 0x65 }, /* CYRILLIC SMALL LETTER YAT -> 'e' */
+ { 0x66, 0x45 }, /* CYRILLIC CAPITAL LETTER LITTLE YUS -> 'E' */
+ { 0x67, 0x65 }, /* CYRILLIC SMALL LETTER LITTLE YUS -> 'e' */
+ { 0x6A, 0x4F }, /* CYRILLIC CAPITAL LETTER BIG YUS -> 'O' */
+ { 0x6B, 0x6F }, /* CYRILLIC SMALL LETTER BIG YUS -> 'o' */
+ { 0x72, 0x46 }, /* CYRILLIC CAPITAL LETTER FITA -> 'F' */
+ { 0x73, 0x66 }, /* CYRILLIC SMALL LETTER FITA -> 'f' */
+ { 0x74, 0x59 }, /* CYRILLIC CAPITAL LETTER IZHITSA -> 'Y' */
+ { 0x75, 0x79 }, /* CYRILLIC SMALL LETTER IZHITSA -> 'y' */
+ { 0x76, 0x59 }, /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT -> 'Y' */
+ { 0x77, 0x79 }, /* CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT -> 'y' */
+ { 0x78, 0x75 }, /* CYRILLIC CAPITAL LETTER UK -> 'u' */
+ { 0x79, 0x75 }, /* CYRILLIC SMALL LETTER UK -> 'u' */
+ { 0x7A, 0x4F }, /* CYRILLIC CAPITAL LETTER ROUND OMEGA -> 'O' */
+ { 0x7B, 0x6F }, /* CYRILLIC SMALL LETTER ROUND OMEGA -> 'o' */
+ { 0x7C, 0x4F }, /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO -> 'O' */
+ { 0x7D, 0x6F }, /* CYRILLIC SMALL LETTER OMEGA WITH TITLO -> 'o' */
+ { 0x80, 0x51 }, /* CYRILLIC CAPITAL LETTER KOPPA -> 'Q' */
+ { 0x81, 0x71 }, /* CYRILLIC SMALL LETTER KOPPA -> 'q' */
+ { 0x8C, 0x22 }, /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN -> '"' */
+ { 0x8D, 0x22 }, /* CYRILLIC SMALL LETTER SEMISOFT SIGN -> '"' */
+ { 0xAE, 0x55 }, /* CYRILLIC CAPITAL LETTER STRAIGHT U -> 'U' */
+ { 0xAF, 0x75 }, /* CYRILLIC SMALL LETTER STRAIGHT U -> 'u' */
+ { 0xBA, 0x48 }, /* CYRILLIC CAPITAL LETTER SHHA -> 'H' */
+ { 0xBB, 0x68 }, /* CYRILLIC SMALL LETTER SHHA -> 'h' */
+ { 0xC0, 0x60 }, /* CYRILLIC LETTER PALOCHKA -> '`' */
+ { 0xD0, 0x61 }, /* CYRILLIC CAPITAL LETTER A WITH BREVE -> 'a' */
+ { 0xD1, 0x61 }, /* CYRILLIC SMALL LETTER A WITH BREVE -> 'a' */
+ { 0xD2, 0x41 }, /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS -> 'A' */
+ { 0xD3, 0x61 }, /* CYRILLIC SMALL LETTER A WITH DIAERESIS -> 'a' */
+ { 0xD8, 0x00 }, /* CYRILLIC CAPITAL LETTER SCHWA -> ... */
+ { 0xDB, 0x40 }, /* CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS -> '@' */
+ { 0xDE, 0x5A }, /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS -> 'Z' */
+ { 0xDF, 0x7A }, /* CYRILLIC SMALL LETTER ZE WITH DIAERESIS -> 'z' */
+ { 0xE2, 0x49 }, /* CYRILLIC CAPITAL LETTER I WITH MACRON -> 'I' */
+ { 0xE3, 0x69 }, /* CYRILLIC SMALL LETTER I WITH MACRON -> 'i' */
+ { 0xE4, 0x49 }, /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS -> 'I' */
+ { 0xE5, 0x69 }, /* CYRILLIC SMALL LETTER I WITH DIAERESIS -> 'i' */
+ { 0xE6, 0x4F }, /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS -> 'O' */
+ { 0xE7, 0x6F }, /* CYRILLIC SMALL LETTER O WITH DIAERESIS -> 'o' */
+ { 0xE8, 0x4F }, /* CYRILLIC CAPITAL LETTER BARRED O -> 'O' */
+ { 0xE9, 0x6F }, /* CYRILLIC SMALL LETTER BARRED O -> 'o' */
+ { 0xEA, 0x4F }, /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS -> 'O' */
+ { 0xEB, 0x6F }, /* CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS -> 'o' */
+ { 0xEC, 0x45 }, /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS -> 'E' */
+ { 0xED, 0x65 }, /* CYRILLIC SMALL LETTER E WITH DIAERESIS -> 'e' */
+ { 0xEE, 0x55 }, /* CYRILLIC CAPITAL LETTER U WITH MACRON -> 'U' */
+ { 0xEF, 0x75 }, /* CYRILLIC SMALL LETTER U WITH MACRON -> 'u' */
+ { 0xF0, 0x55 }, /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS -> 'U' */
+ { 0xF1, 0x75 }, /* CYRILLIC SMALL LETTER U WITH DIAERESIS -> 'u' */
+ { 0xF2, 0x55 }, /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE -> 'U' */
+ { 0xF3, 0x75 }, /* CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE -> 'u' */
+ { 0xF8, 0x59 }, /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS -> 'Y' */
+ { 0xF9, 0x79 }, /* CYRILLIC SMALL LETTER YERU WITH DIAERESIS -> 'y' */
+ /* Entries for page 0x05 */
+ { 0x31, 0x41 }, /* ARMENIAN CAPITAL LETTER AYB -> 'A' */
+ { 0x32, 0x42 }, /* ARMENIAN CAPITAL LETTER BEN -> 'B' */
+ { 0x33, 0x47 }, /* ARMENIAN CAPITAL LETTER GIM -> 'G' */
+ { 0x34, 0x44 }, /* ARMENIAN CAPITAL LETTER DA -> 'D' */
+ { 0x35, 0x45 }, /* ARMENIAN CAPITAL LETTER ECH -> 'E' */
+ { 0x36, 0x5A }, /* ARMENIAN CAPITAL LETTER ZA -> 'Z' */
+ { 0x37, 0x45 }, /* ARMENIAN CAPITAL LETTER EH -> 'E' */
+ { 0x38, 0x45 }, /* ARMENIAN CAPITAL LETTER ET -> 'E' */
+ { 0x3B, 0x49 }, /* ARMENIAN CAPITAL LETTER INI -> 'I' */
+ { 0x3C, 0x4C }, /* ARMENIAN CAPITAL LETTER LIWN -> 'L' */
+ { 0x3F, 0x4B }, /* ARMENIAN CAPITAL LETTER KEN -> 'K' */
+ { 0x40, 0x48 }, /* ARMENIAN CAPITAL LETTER HO -> 'H' */
+ { 0x44, 0x4D }, /* ARMENIAN CAPITAL LETTER MEN -> 'M' */
+ { 0x45, 0x59 }, /* ARMENIAN CAPITAL LETTER YI -> 'Y' */
+ { 0x46, 0x4E }, /* ARMENIAN CAPITAL LETTER NOW -> 'N' */
+ { 0x48, 0x4F }, /* ARMENIAN CAPITAL LETTER VO -> 'O' */
+ { 0x4A, 0x50 }, /* ARMENIAN CAPITAL LETTER PEH -> 'P' */
+ { 0x4B, 0x4A }, /* ARMENIAN CAPITAL LETTER JHEH -> 'J' */
+ { 0x4D, 0x53 }, /* ARMENIAN CAPITAL LETTER SEH -> 'S' */
+ { 0x4E, 0x56 }, /* ARMENIAN CAPITAL LETTER VEW -> 'V' */
+ { 0x4F, 0x54 }, /* ARMENIAN CAPITAL LETTER TIWN -> 'T' */
+ { 0x50, 0x52 }, /* ARMENIAN CAPITAL LETTER REH -> 'R' */
+ { 0x52, 0x57 }, /* ARMENIAN CAPITAL LETTER YIWN -> 'W' */
+ { 0x55, 0x4F }, /* ARMENIAN CAPITAL LETTER OH -> 'O' */
+ { 0x56, 0x46 }, /* ARMENIAN CAPITAL LETTER FEH -> 'F' */
+ { 0x59, 0x3C }, /* ARMENIAN MODIFIER LETTER LEFT HALF RING -> '<' */
+ { 0x5A, 0x27 }, /* ARMENIAN APOSTROPHE -> ''' */
+ { 0x5B, 0x2F }, /* ARMENIAN EMPHASIS MARK -> '/' */
+ { 0x5C, 0x21 }, /* ARMENIAN EXCLAMATION MARK -> '!' */
+ { 0x5D, 0x2C }, /* ARMENIAN COMMA -> ',' */
+ { 0x5E, 0x3F }, /* ARMENIAN QUESTION MARK -> '?' */
+ { 0x5F, 0x2E }, /* ARMENIAN ABBREVIATION MARK -> '.' */
+ { 0x61, 0x61 }, /* ARMENIAN SMALL LETTER AYB -> 'a' */
+ { 0x62, 0x62 }, /* ARMENIAN SMALL LETTER BEN -> 'b' */
+ { 0x63, 0x67 }, /* ARMENIAN SMALL LETTER GIM -> 'g' */
+ { 0x64, 0x64 }, /* ARMENIAN SMALL LETTER DA -> 'd' */
+ { 0x65, 0x65 }, /* ARMENIAN SMALL LETTER ECH -> 'e' */
+ { 0x66, 0x7A }, /* ARMENIAN SMALL LETTER ZA -> 'z' */
+ { 0x67, 0x65 }, /* ARMENIAN SMALL LETTER EH -> 'e' */
+ { 0x68, 0x65 }, /* ARMENIAN SMALL LETTER ET -> 'e' */
+ { 0x6B, 0x69 }, /* ARMENIAN SMALL LETTER INI -> 'i' */
+ { 0x6C, 0x6C }, /* ARMENIAN SMALL LETTER LIWN -> 'l' */
+ { 0x6F, 0x6B }, /* ARMENIAN SMALL LETTER KEN -> 'k' */
+ { 0x70, 0x68 }, /* ARMENIAN SMALL LETTER HO -> 'h' */
+ { 0x74, 0x6D }, /* ARMENIAN SMALL LETTER MEN -> 'm' */
+ { 0x75, 0x79 }, /* ARMENIAN SMALL LETTER YI -> 'y' */
+ { 0x76, 0x6E }, /* ARMENIAN SMALL LETTER NOW -> 'n' */
+ { 0x78, 0x6F }, /* ARMENIAN SMALL LETTER VO -> 'o' */
+ { 0x7A, 0x70 }, /* ARMENIAN SMALL LETTER PEH -> 'p' */
+ { 0x7B, 0x6A }, /* ARMENIAN SMALL LETTER JHEH -> 'j' */
+ { 0x7D, 0x73 }, /* ARMENIAN SMALL LETTER SEH -> 's' */
+ { 0x7E, 0x76 }, /* ARMENIAN SMALL LETTER VEW -> 'v' */
+ { 0x7F, 0x74 }, /* ARMENIAN SMALL LETTER TIWN -> 't' */
+ { 0x80, 0x72 }, /* ARMENIAN SMALL LETTER REH -> 'r' */
+ { 0x82, 0x77 }, /* ARMENIAN SMALL LETTER YIWN -> 'w' */
+ { 0x85, 0x6F }, /* ARMENIAN SMALL LETTER OH -> 'o' */
+ { 0x86, 0x66 }, /* ARMENIAN SMALL LETTER FEH -> 'f' */
+ { 0x89, 0x3A }, /* ARMENIAN FULL STOP -> ':' */
+ { 0x8A, 0x2D }, /* ARMENIAN HYPHEN -> '-' */
+ { 0xB1, 0x65 }, /* HEBREW POINT HATAF SEGOL -> 'e' */
+ { 0xB2, 0x61 }, /* HEBREW POINT HATAF PATAH -> 'a' */
+ { 0xB3, 0x6F }, /* HEBREW POINT HATAF QAMATS -> 'o' */
+ { 0xB4, 0x69 }, /* HEBREW POINT HIRIQ -> 'i' */
+ { 0xB5, 0x65 }, /* HEBREW POINT TSERE -> 'e' */
+ { 0xB6, 0x65 }, /* HEBREW POINT SEGOL -> 'e' */
+ { 0xB7, 0x61 }, /* HEBREW POINT PATAH -> 'a' */
+ { 0xB8, 0x61 }, /* HEBREW POINT QAMATS -> 'a' */
+ { 0xB9, 0x6F }, /* HEBREW POINT HOLAM -> 'o' */
+ { 0xBA, 0x6F }, /* HEBREW POINT HOLAM HASER FOR VAV -> 'o' */
+ { 0xBB, 0x75 }, /* HEBREW POINT QUBUTS -> 'u' */
+ { 0xBE, 0x2D }, /* HEBREW PUNCTUATION MAQAF -> '-' */
+ { 0xC0, 0x7C }, /* HEBREW PUNCTUATION PASEQ -> '|' */
+ { 0xC3, 0x2E }, /* HEBREW PUNCTUATION SOF PASUQ -> '.' */
+ { 0xC6, 0x6E }, /* HEBREW PUNCTUATION NUN HAFUKHA -> 'n' */
+ { 0xC7, 0x6F }, /* HEBREW POINT QAMATS QATAN -> 'o' */
+ { 0xD0, 0x41 }, /* HEBREW LETTER ALEF -> 'A' */
+ { 0xD1, 0x62 }, /* HEBREW LETTER BET -> 'b' */
+ { 0xD2, 0x67 }, /* HEBREW LETTER GIMEL -> 'g' */
+ { 0xD3, 0x64 }, /* HEBREW LETTER DALET -> 'd' */
+ { 0xD4, 0x68 }, /* HEBREW LETTER HE -> 'h' */
+ { 0xD5, 0x76 }, /* HEBREW LETTER VAV -> 'v' */
+ { 0xD6, 0x7A }, /* HEBREW LETTER ZAYIN -> 'z' */
+ { 0xD7, 0x48 }, /* HEBREW LETTER HET -> 'H' */
+ { 0xD8, 0x54 }, /* HEBREW LETTER TET -> 'T' */
+ { 0xD9, 0x79 }, /* HEBREW LETTER YOD -> 'y' */
+ { 0xDC, 0x6C }, /* HEBREW LETTER LAMED -> 'l' */
+ { 0xDD, 0x6D }, /* HEBREW LETTER FINAL MEM -> 'm' */
+ { 0xDE, 0x6D }, /* HEBREW LETTER MEM -> 'm' */
+ { 0xDF, 0x6E }, /* HEBREW LETTER FINAL NUN -> 'n' */
+ { 0xE0, 0x6E }, /* HEBREW LETTER NUN -> 'n' */
+ { 0xE1, 0x73 }, /* HEBREW LETTER SAMEKH -> 's' */
+ { 0xE2, 0x60 }, /* HEBREW LETTER AYIN -> '`' */
+ { 0xE3, 0x70 }, /* HEBREW LETTER FINAL PE -> 'p' */
+ { 0xE4, 0x70 }, /* HEBREW LETTER PE -> 'p' */
+ { 0xE7, 0x6B }, /* HEBREW LETTER QOF -> 'k' */
+ { 0xE8, 0x72 }, /* HEBREW LETTER RESH -> 'r' */
+ { 0xEA, 0x74 }, /* HEBREW LETTER TAV -> 't' */
+ { 0xF0, 0x56 }, /* HEBREW LIGATURE YIDDISH DOUBLE VAV -> 'V' */
+ { 0xF3, 0x27 }, /* HEBREW PUNCTUATION GERESH -> ''' */
+ { 0xF4, 0x22 }, /* HEBREW PUNCTUATION GERSHAYIM -> '"' */
+ /* Entries for page 0x06 */
+ { 0x0C, 0x2C }, /* ARABIC COMMA -> ',' */
+ { 0x1B, 0x3B }, /* ARABIC SEMICOLON -> ';' */
+ { 0x1F, 0x3F }, /* ARABIC QUESTION MARK -> '?' */
+ { 0x22, 0x61 }, /* ARABIC LETTER ALEF WITH MADDA ABOVE -> 'a' */
+ { 0x23, 0x27 }, /* ARABIC LETTER ALEF WITH HAMZA ABOVE -> ''' */
+ { 0x28, 0x62 }, /* ARABIC LETTER BEH -> 'b' */
+ { 0x29, 0x40 }, /* ARABIC LETTER TEH MARBUTA -> '@' */
+ { 0x2A, 0x74 }, /* ARABIC LETTER TEH -> 't' */
+ { 0x2C, 0x6A }, /* ARABIC LETTER JEEM -> 'j' */
+ { 0x2D, 0x48 }, /* ARABIC LETTER HAH -> 'H' */
+ { 0x2F, 0x64 }, /* ARABIC LETTER DAL -> 'd' */
+ { 0x31, 0x72 }, /* ARABIC LETTER REH -> 'r' */
+ { 0x32, 0x7A }, /* ARABIC LETTER ZAIN -> 'z' */
+ { 0x33, 0x73 }, /* ARABIC LETTER SEEN -> 's' */
+ { 0x35, 0x53 }, /* ARABIC LETTER SAD -> 'S' */
+ { 0x36, 0x44 }, /* ARABIC LETTER DAD -> 'D' */
+ { 0x37, 0x54 }, /* ARABIC LETTER TAH -> 'T' */
+ { 0x38, 0x5A }, /* ARABIC LETTER ZAH -> 'Z' */
+ { 0x39, 0x60 }, /* ARABIC LETTER AIN -> '`' */
+ { 0x3A, 0x47 }, /* ARABIC LETTER GHAIN -> 'G' */
+ { 0x41, 0x66 }, /* ARABIC LETTER FEH -> 'f' */
+ { 0x42, 0x71 }, /* ARABIC LETTER QAF -> 'q' */
+ { 0x43, 0x6B }, /* ARABIC LETTER KAF -> 'k' */
+ { 0x44, 0x6C }, /* ARABIC LETTER LAM -> 'l' */
+ { 0x45, 0x6D }, /* ARABIC LETTER MEEM -> 'm' */
+ { 0x46, 0x6E }, /* ARABIC LETTER NOON -> 'n' */
+ { 0x47, 0x68 }, /* ARABIC LETTER HEH -> 'h' */
+ { 0x48, 0x77 }, /* ARABIC LETTER WAW -> 'w' */
+ { 0x49, 0x7E }, /* ARABIC LETTER ALEF MAKSURA -> '~' */
+ { 0x4A, 0x79 }, /* ARABIC LETTER YEH -> 'y' */
+ { 0x4E, 0x61 }, /* ARABIC FATHA -> 'a' */
+ { 0x4F, 0x75 }, /* ARABIC DAMMA -> 'u' */
+ { 0x50, 0x69 }, /* ARABIC KASRA -> 'i' */
+ { 0x51, 0x57 }, /* ARABIC SHADDA -> 'W' */
+ { 0x54, 0x27 }, /* ARABIC HAMZA ABOVE -> ''' */
+ { 0x55, 0x27 }, /* ARABIC HAMZA BELOW -> ''' */
+ { 0x60, 0x30 }, /* ARABIC-INDIC DIGIT ZERO -> '0' */
+ { 0x61, 0x31 }, /* ARABIC-INDIC DIGIT ONE -> '1' */
+ { 0x62, 0x32 }, /* ARABIC-INDIC DIGIT TWO -> '2' */
+ { 0x63, 0x33 }, /* ARABIC-INDIC DIGIT THREE -> '3' */
+ { 0x64, 0x34 }, /* ARABIC-INDIC DIGIT FOUR -> '4' */
+ { 0x65, 0x35 }, /* ARABIC-INDIC DIGIT FIVE -> '5' */
+ { 0x66, 0x36 }, /* ARABIC-INDIC DIGIT SIX -> '6' */
+ { 0x67, 0x37 }, /* ARABIC-INDIC DIGIT SEVEN -> '7' */
+ { 0x68, 0x38 }, /* ARABIC-INDIC DIGIT EIGHT -> '8' */
+ { 0x69, 0x39 }, /* ARABIC-INDIC DIGIT NINE -> '9' */
+ { 0x6A, 0x25 }, /* ARABIC PERCENT SIGN -> '%' */
+ { 0x6B, 0x2E }, /* ARABIC DECIMAL SEPARATOR -> '.' */
+ { 0x6C, 0x2C }, /* ARABIC THOUSANDS SEPARATOR -> ',' */
+ { 0x6D, 0x2A }, /* ARABIC FIVE POINTED STAR -> '*' */
+ { 0x71, 0x00 }, /* ARABIC LETTER ALEF WASLA -> ... */
+ { 0x73, 0x27 }, /* ARABIC LETTER ALEF WITH WAVY HAMZA BELOW -> ''' */
+ { 0x75, 0x27 }, /* ARABIC LETTER HIGH HAMZA ALEF -> ''' */
+ { 0x7B, 0x62 }, /* ARABIC LETTER BEEH -> 'b' */
+ { 0x7C, 0x74 }, /* ARABIC LETTER TEH WITH RING -> 't' */
+ { 0x7D, 0x54 }, /* ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS -> 'T' */
+ { 0x7E, 0x70 }, /* ARABIC LETTER PEH -> 'p' */
+ { 0x82, 0x48 }, /* ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE -> 'H' */
+ { 0x85, 0x48 }, /* ARABIC LETTER HAH WITH THREE DOTS ABOVE -> 'H' */
+ { 0x89, 0x44 }, /* ARABIC LETTER DAL WITH RING -> 'D' */
+ { 0x8A, 0x44 }, /* ARABIC LETTER DAL WITH DOT BELOW -> 'D' */
+ { 0x8E, 0x64 }, /* ARABIC LETTER DUL -> 'd' */
+ { 0x8F, 0x44 }, /* ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS -> 'D' */
+ { 0x90, 0x44 }, /* ARABIC LETTER DAL WITH FOUR DOTS ABOVE -> 'D' */
+ { 0x92, 0x00 }, /* ARABIC LETTER REH WITH SMALL V -> ... */
+ { 0x97, 0x52 }, /* ARABIC LETTER REH WITH TWO DOTS ABOVE -> 'R' */
+ { 0x98, 0x6A }, /* ARABIC LETTER JEH -> 'j' */
+ { 0x99, 0x52 }, /* ARABIC LETTER REH WITH FOUR DOTS ABOVE -> 'R' */
+ { 0x9A, 0x00 }, /* ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE -> ... */
+ { 0x9E, 0x53 }, /* ARABIC LETTER SAD WITH THREE DOTS ABOVE -> 'S' */
+ { 0x9F, 0x54 }, /* ARABIC LETTER TAH WITH THREE DOTS ABOVE -> 'T' */
+ { 0xA1, 0x00 }, /* ARABIC LETTER DOTLESS FEH -> ... */
+ { 0xA3, 0x46 }, /* ARABIC LETTER FEH WITH DOT BELOW -> 'F' */
+ { 0xA4, 0x76 }, /* ARABIC LETTER VEH -> 'v' */
+ { 0xA5, 0x66 }, /* ARABIC LETTER FEH WITH THREE DOTS BELOW -> 'f' */
+ { 0xA7, 0x51 }, /* ARABIC LETTER QAF WITH DOT ABOVE -> 'Q' */
+ { 0xA8, 0x51 }, /* ARABIC LETTER QAF WITH THREE DOTS ABOVE -> 'Q' */
+ { 0xAA, 0x6B }, /* ARABIC LETTER SWASH KAF -> 'k' */
+ { 0xAB, 0x4B }, /* ARABIC LETTER KAF WITH RING -> 'K' */
+ { 0xAC, 0x4B }, /* ARABIC LETTER KAF WITH DOT ABOVE -> 'K' */
+ { 0xAE, 0x4B }, /* ARABIC LETTER KAF WITH THREE DOTS BELOW -> 'K' */
+ { 0xAF, 0x67 }, /* ARABIC LETTER GAF -> 'g' */
+ { 0xB0, 0x47 }, /* ARABIC LETTER GAF WITH RING -> 'G' */
+ { 0xB1, 0x4E }, /* ARABIC LETTER NGOEH -> 'N' */
+ { 0xB2, 0x00 }, /* ARABIC LETTER GAF WITH TWO DOTS BELOW -> ... */
+ { 0xB4, 0x47 }, /* ARABIC LETTER GAF WITH THREE DOTS ABOVE -> 'G' */
+ { 0xB5, 0x00 }, /* ARABIC LETTER LAM WITH SMALL V -> ... */
+ { 0xB8, 0x4C }, /* ARABIC LETTER LAM WITH THREE DOTS BELOW -> 'L' */
+ { 0xB9, 0x00 }, /* ARABIC LETTER NOON WITH DOT BELOW -> ... */
+ { 0xBD, 0x4E }, /* ARABIC LETTER NOON WITH THREE DOTS ABOVE -> 'N' */
+ { 0xBE, 0x68 }, /* ARABIC LETTER HEH DOACHASHMEE -> 'h' */
+ { 0xC1, 0x68 }, /* ARABIC LETTER HEH GOAL -> 'h' */
+ { 0xC2, 0x48 }, /* ARABIC LETTER HEH GOAL WITH HAMZA ABOVE -> 'H' */
+ { 0xC3, 0x40 }, /* ARABIC LETTER TEH MARBUTA GOAL -> '@' */
+ { 0xC4, 0x57 }, /* ARABIC LETTER WAW WITH RING -> 'W' */
+ { 0xC7, 0x75 }, /* ARABIC LETTER U -> 'u' */
+ { 0xCA, 0x57 }, /* ARABIC LETTER WAW WITH TWO DOTS ABOVE -> 'W' */
+ { 0xCB, 0x76 }, /* ARABIC LETTER VE -> 'v' */
+ { 0xCC, 0x79 }, /* ARABIC LETTER FARSI YEH -> 'y' */
+ { 0xCD, 0x59 }, /* ARABIC LETTER YEH WITH TAIL -> 'Y' */
+ { 0xCE, 0x59 }, /* ARABIC LETTER YEH WITH SMALL V -> 'Y' */
+ { 0xCF, 0x57 }, /* ARABIC LETTER WAW WITH DOT ABOVE -> 'W' */
+ { 0xD2, 0x79 }, /* ARABIC LETTER YEH BARREE -> 'y' */
+ { 0xD4, 0x2E }, /* ARABIC FULL STOP -> '.' */
+ { 0xDD, 0x40 }, /* ARABIC END OF AYAH -> '@' */
+ { 0xDE, 0x23 }, /* ARABIC START OF RUB EL HIZB -> '#' */
+ { 0xE9, 0x5E }, /* ARABIC PLACE OF SAJDAH -> '^' */
+ { 0xF0, 0x30 }, /* EXTENDED ARABIC-INDIC DIGIT ZERO -> '0' */
+ { 0xF1, 0x31 }, /* EXTENDED ARABIC-INDIC DIGIT ONE -> '1' */
+ { 0xF2, 0x32 }, /* EXTENDED ARABIC-INDIC DIGIT TWO -> '2' */
+ { 0xF3, 0x33 }, /* EXTENDED ARABIC-INDIC DIGIT THREE -> '3' */
+ { 0xF4, 0x34 }, /* EXTENDED ARABIC-INDIC DIGIT FOUR -> '4' */
+ { 0xF5, 0x35 }, /* EXTENDED ARABIC-INDIC DIGIT FIVE -> '5' */
+ { 0xF6, 0x36 }, /* EXTENDED ARABIC-INDIC DIGIT SIX -> '6' */
+ { 0xF7, 0x37 }, /* EXTENDED ARABIC-INDIC DIGIT SEVEN -> '7' */
+ { 0xF8, 0x38 }, /* EXTENDED ARABIC-INDIC DIGIT EIGHT -> '8' */
+ { 0xF9, 0x39 }, /* EXTENDED ARABIC-INDIC DIGIT NINE -> '9' */
+ { 0xFB, 0x44 }, /* ARABIC LETTER DAD WITH DOT BELOW -> 'D' */
+ { 0xFD, 0x26 }, /* ARABIC SIGN SINDHI AMPERSAND -> '&' */
+ /* Entries for page 0x07 */
+ { 0x01, 0x2F }, /* SYRIAC SUPRALINEAR FULL STOP -> '/' */
+ { 0x02, 0x2C }, /* SYRIAC SUBLINEAR FULL STOP -> ',' */
+ { 0x03, 0x21 }, /* SYRIAC SUPRALINEAR COLON -> '!' */
+ { 0x04, 0x21 }, /* SYRIAC SUBLINEAR COLON -> '!' */
+ { 0x05, 0x2D }, /* SYRIAC HORIZONTAL COLON -> '-' */
+ { 0x06, 0x2C }, /* SYRIAC COLON SKEWED LEFT -> ',' */
+ { 0x07, 0x2C }, /* SYRIAC COLON SKEWED RIGHT -> ',' */
+ { 0x08, 0x3B }, /* SYRIAC SUPRALINEAR COLON SKEWED LEFT -> ';' */
+ { 0x09, 0x3F }, /* SYRIAC SUBLINEAR COLON SKEWED RIGHT -> '?' */
+ { 0x0A, 0x7E }, /* SYRIAC CONTRACTION -> '~' */
+ { 0x0B, 0x7B }, /* SYRIAC HARKLEAN OBELUS -> '{' */
+ { 0x0C, 0x7D }, /* SYRIAC HARKLEAN METOBELUS -> '}' */
+ { 0x0D, 0x2A }, /* SYRIAC HARKLEAN ASTERISCUS -> '*' */
+ { 0x10, 0x27 }, /* SYRIAC LETTER ALAPH -> ''' */
+ { 0x12, 0x62 }, /* SYRIAC LETTER BETH -> 'b' */
+ { 0x13, 0x67 }, /* SYRIAC LETTER GAMAL -> 'g' */
+ { 0x14, 0x67 }, /* SYRIAC LETTER GAMAL GARSHUNI -> 'g' */
+ { 0x15, 0x64 }, /* SYRIAC LETTER DALATH -> 'd' */
+ { 0x16, 0x64 }, /* SYRIAC LETTER DOTLESS DALATH RISH -> 'd' */
+ { 0x17, 0x68 }, /* SYRIAC LETTER HE -> 'h' */
+ { 0x18, 0x77 }, /* SYRIAC LETTER WAW -> 'w' */
+ { 0x19, 0x7A }, /* SYRIAC LETTER ZAIN -> 'z' */
+ { 0x1A, 0x48 }, /* SYRIAC LETTER HETH -> 'H' */
+ { 0x1B, 0x74 }, /* SYRIAC LETTER TETH -> 't' */
+ { 0x1C, 0x74 }, /* SYRIAC LETTER TETH GARSHUNI -> 't' */
+ { 0x1D, 0x79 }, /* SYRIAC LETTER YUDH -> 'y' */
+ { 0x1F, 0x6B }, /* SYRIAC LETTER KAPH -> 'k' */
+ { 0x20, 0x6C }, /* SYRIAC LETTER LAMADH -> 'l' */
+ { 0x21, 0x6D }, /* SYRIAC LETTER MIM -> 'm' */
+ { 0x22, 0x6E }, /* SYRIAC LETTER NUN -> 'n' */
+ { 0x23, 0x73 }, /* SYRIAC LETTER SEMKATH -> 's' */
+ { 0x24, 0x73 }, /* SYRIAC LETTER FINAL SEMKATH -> 's' */
+ { 0x25, 0x60 }, /* SYRIAC LETTER E -> '`' */
+ { 0x26, 0x70 }, /* SYRIAC LETTER PE -> 'p' */
+ { 0x27, 0x70 }, /* SYRIAC LETTER REVERSED PE -> 'p' */
+ { 0x28, 0x53 }, /* SYRIAC LETTER SADHE -> 'S' */
+ { 0x29, 0x71 }, /* SYRIAC LETTER QAPH -> 'q' */
+ { 0x2A, 0x72 }, /* SYRIAC LETTER RISH -> 'r' */
+ { 0x2C, 0x74 }, /* SYRIAC LETTER TAW -> 't' */
+ { 0x30, 0x00 }, /* SYRIAC PTHAHA ABOVE -> ... */
+ { 0x32, 0x61 }, /* SYRIAC PTHAHA DOTTED -> 'a' */
+ { 0x33, 0x00 }, /* SYRIAC ZQAPHA ABOVE -> ... */
+ { 0x35, 0x41 }, /* SYRIAC ZQAPHA DOTTED -> 'A' */
+ { 0x36, 0x00 }, /* SYRIAC RBASA ABOVE -> ... */
+ { 0x38, 0x65 }, /* SYRIAC DOTTED ZLAMA HORIZONTAL -> 'e' */
+ { 0x39, 0x45 }, /* SYRIAC DOTTED ZLAMA ANGULAR -> 'E' */
+ { 0x3A, 0x69 }, /* SYRIAC HBASA ABOVE -> 'i' */
+ { 0x3B, 0x69 }, /* SYRIAC HBASA BELOW -> 'i' */
+ { 0x3C, 0x00 }, /* SYRIAC HBASA-ESASA DOTTED -> ... */
+ { 0x3E, 0x75 }, /* SYRIAC ESASA BELOW -> 'u' */
+ { 0x3F, 0x6F }, /* SYRIAC RWAHA -> 'o' */
+ { 0x41, 0x60 }, /* SYRIAC QUSHSHAYA -> '`' */
+ { 0x42, 0x27 }, /* SYRIAC RUKKAKHA -> ''' */
+ { 0x45, 0x58 }, /* SYRIAC THREE DOTS ABOVE -> 'X' */
+ { 0x46, 0x51 }, /* SYRIAC THREE DOTS BELOW -> 'Q' */
+ { 0x47, 0x40 }, /* SYRIAC OBLIQUE LINE ABOVE -> '@' */
+ { 0x48, 0x40 }, /* SYRIAC OBLIQUE LINE BELOW -> '@' */
+ { 0x49, 0x7C }, /* SYRIAC MUSIC -> '|' */
+ { 0x4A, 0x2B }, /* SYRIAC BARREKH -> '+' */
+ { 0x80, 0x68 }, /* THAANA LETTER HAA -> 'h' */
+ { 0x82, 0x6E }, /* THAANA LETTER NOONU -> 'n' */
+ { 0x83, 0x72 }, /* THAANA LETTER RAA -> 'r' */
+ { 0x84, 0x62 }, /* THAANA LETTER BAA -> 'b' */
+ { 0x85, 0x4C }, /* THAANA LETTER LHAVIYANI -> 'L' */
+ { 0x86, 0x6B }, /* THAANA LETTER KAAFU -> 'k' */
+ { 0x87, 0x27 }, /* THAANA LETTER ALIFU -> ''' */
+ { 0x88, 0x76 }, /* THAANA LETTER VAAVU -> 'v' */
+ { 0x89, 0x6D }, /* THAANA LETTER MEEMU -> 'm' */
+ { 0x8A, 0x66 }, /* THAANA LETTER FAAFU -> 'f' */
+ { 0x8D, 0x6C }, /* THAANA LETTER LAAMU -> 'l' */
+ { 0x8E, 0x67 }, /* THAANA LETTER GAAFU -> 'g' */
+ { 0x90, 0x73 }, /* THAANA LETTER SEENU -> 's' */
+ { 0x91, 0x64 }, /* THAANA LETTER DAVIYANI -> 'd' */
+ { 0x92, 0x7A }, /* THAANA LETTER ZAVIYANI -> 'z' */
+ { 0x93, 0x74 }, /* THAANA LETTER TAVIYANI -> 't' */
+ { 0x94, 0x79 }, /* THAANA LETTER YAA -> 'y' */
+ { 0x95, 0x70 }, /* THAANA LETTER PAVIYANI -> 'p' */
+ { 0x96, 0x6A }, /* THAANA LETTER JAVIYANI -> 'j' */
+ { 0x9C, 0x7A }, /* THAANA LETTER ZAA -> 'z' */
+ { 0x9E, 0x73 }, /* THAANA LETTER SAADHU -> 's' */
+ { 0x9F, 0x64 }, /* THAANA LETTER DAADHU -> 'd' */
+ { 0xA0, 0x74 }, /* THAANA LETTER TO -> 't' */
+ { 0xA1, 0x7A }, /* THAANA LETTER ZO -> 'z' */
+ { 0xA2, 0x60 }, /* THAANA LETTER AINU -> '`' */
+ { 0xA4, 0x71 }, /* THAANA LETTER QAAFU -> 'q' */
+ { 0xA5, 0x77 }, /* THAANA LETTER WAAVU -> 'w' */
+ { 0xA6, 0x61 }, /* THAANA ABAFILI -> 'a' */
+ { 0xA8, 0x69 }, /* THAANA IBIFILI -> 'i' */
+ { 0xAA, 0x75 }, /* THAANA UBUFILI -> 'u' */
+ { 0xAC, 0x65 }, /* THAANA EBEFILI -> 'e' */
+ { 0xAE, 0x6F }, /* THAANA OBOFILI -> 'o' */
+ /* Entries for page 0x09 */
+ { 0x01, 0x4E }, /* DEVANAGARI SIGN CANDRABINDU -> 'N' */
+ { 0x02, 0x4E }, /* DEVANAGARI SIGN ANUSVARA -> 'N' */
+ { 0x03, 0x48 }, /* DEVANAGARI SIGN VISARGA -> 'H' */
+ { 0x05, 0x61 }, /* DEVANAGARI LETTER A -> 'a' */
+ { 0x07, 0x69 }, /* DEVANAGARI LETTER I -> 'i' */
+ { 0x09, 0x75 }, /* DEVANAGARI LETTER U -> 'u' */
+ { 0x0B, 0x52 }, /* DEVANAGARI LETTER VOCALIC R -> 'R' */
+ { 0x0C, 0x4C }, /* DEVANAGARI LETTER VOCALIC L -> 'L' */
+ { 0x0E, 0x65 }, /* DEVANAGARI LETTER SHORT E -> 'e' */
+ { 0x0F, 0x65 }, /* DEVANAGARI LETTER E -> 'e' */
+ { 0x12, 0x6F }, /* DEVANAGARI LETTER SHORT O -> 'o' */
+ { 0x13, 0x6F }, /* DEVANAGARI LETTER O -> 'o' */
+ { 0x15, 0x6B }, /* DEVANAGARI LETTER KA -> 'k' */
+ { 0x17, 0x67 }, /* DEVANAGARI LETTER GA -> 'g' */
+ { 0x1A, 0x63 }, /* DEVANAGARI LETTER CA -> 'c' */
+ { 0x1C, 0x6A }, /* DEVANAGARI LETTER JA -> 'j' */
+ { 0x24, 0x74 }, /* DEVANAGARI LETTER TA -> 't' */
+ { 0x26, 0x64 }, /* DEVANAGARI LETTER DA -> 'd' */
+ { 0x28, 0x6E }, /* DEVANAGARI LETTER NA -> 'n' */
+ { 0x2A, 0x70 }, /* DEVANAGARI LETTER PA -> 'p' */
+ { 0x2C, 0x62 }, /* DEVANAGARI LETTER BA -> 'b' */
+ { 0x2E, 0x6D }, /* DEVANAGARI LETTER MA -> 'm' */
+ { 0x2F, 0x79 }, /* DEVANAGARI LETTER YA -> 'y' */
+ { 0x30, 0x72 }, /* DEVANAGARI LETTER RA -> 'r' */
+ { 0x32, 0x6C }, /* DEVANAGARI LETTER LA -> 'l' */
+ { 0x33, 0x6C }, /* DEVANAGARI LETTER LLA -> 'l' */
+ { 0x35, 0x76 }, /* DEVANAGARI LETTER VA -> 'v' */
+ { 0x38, 0x73 }, /* DEVANAGARI LETTER SA -> 's' */
+ { 0x39, 0x68 }, /* DEVANAGARI LETTER HA -> 'h' */
+ { 0x3C, 0x27 }, /* DEVANAGARI SIGN NUKTA -> ''' */
+ { 0x3D, 0x27 }, /* DEVANAGARI SIGN AVAGRAHA -> ''' */
+ { 0x3F, 0x69 }, /* DEVANAGARI VOWEL SIGN I -> 'i' */
+ { 0x41, 0x75 }, /* DEVANAGARI VOWEL SIGN U -> 'u' */
+ { 0x43, 0x52 }, /* DEVANAGARI VOWEL SIGN VOCALIC R -> 'R' */
+ { 0x46, 0x65 }, /* DEVANAGARI VOWEL SIGN SHORT E -> 'e' */
+ { 0x47, 0x65 }, /* DEVANAGARI VOWEL SIGN E -> 'e' */
+ { 0x4A, 0x6F }, /* DEVANAGARI VOWEL SIGN SHORT O -> 'o' */
+ { 0x4B, 0x6F }, /* DEVANAGARI VOWEL SIGN O -> 'o' */
+ { 0x51, 0x27 }, /* DEVANAGARI STRESS SIGN UDATTA -> ''' */
+ { 0x52, 0x27 }, /* DEVANAGARI STRESS SIGN ANUDATTA -> ''' */
+ { 0x53, 0x60 }, /* DEVANAGARI GRAVE ACCENT -> '`' */
+ { 0x54, 0x27 }, /* DEVANAGARI ACUTE ACCENT -> ''' */
+ { 0x58, 0x71 }, /* DEVANAGARI LETTER QA -> 'q' */
+ { 0x5B, 0x7A }, /* DEVANAGARI LETTER ZA -> 'z' */
+ { 0x5E, 0x66 }, /* DEVANAGARI LETTER FA -> 'f' */
+ { 0x62, 0x4C }, /* DEVANAGARI VOWEL SIGN VOCALIC L -> 'L' */
+ { 0x66, 0x30 }, /* DEVANAGARI DIGIT ZERO -> '0' */
+ { 0x67, 0x31 }, /* DEVANAGARI DIGIT ONE -> '1' */
+ { 0x68, 0x32 }, /* DEVANAGARI DIGIT TWO -> '2' */
+ { 0x69, 0x33 }, /* DEVANAGARI DIGIT THREE -> '3' */
+ { 0x6A, 0x34 }, /* DEVANAGARI DIGIT FOUR -> '4' */
+ { 0x6B, 0x35 }, /* DEVANAGARI DIGIT FIVE -> '5' */
+ { 0x6C, 0x36 }, /* DEVANAGARI DIGIT SIX -> '6' */
+ { 0x6D, 0x37 }, /* DEVANAGARI DIGIT SEVEN -> '7' */
+ { 0x6E, 0x38 }, /* DEVANAGARI DIGIT EIGHT -> '8' */
+ { 0x6F, 0x39 }, /* DEVANAGARI DIGIT NINE -> '9' */
+ { 0x70, 0x2E }, /* DEVANAGARI ABBREVIATION SIGN -> '.' */
+ { 0x81, 0x4E }, /* BENGALI SIGN CANDRABINDU -> 'N' */
+ { 0x82, 0x4E }, /* BENGALI SIGN ANUSVARA -> 'N' */
+ { 0x83, 0x48 }, /* BENGALI SIGN VISARGA -> 'H' */
+ { 0x85, 0x61 }, /* BENGALI LETTER A -> 'a' */
+ { 0x87, 0x69 }, /* BENGALI LETTER I -> 'i' */
+ { 0x89, 0x75 }, /* BENGALI LETTER U -> 'u' */
+ { 0x8B, 0x52 }, /* BENGALI LETTER VOCALIC R -> 'R' */
+ { 0x8F, 0x65 }, /* BENGALI LETTER E -> 'e' */
+ { 0x93, 0x6F }, /* BENGALI LETTER O -> 'o' */
+ { 0x95, 0x6B }, /* BENGALI LETTER KA -> 'k' */
+ { 0x97, 0x67 }, /* BENGALI LETTER GA -> 'g' */
+ { 0x9A, 0x63 }, /* BENGALI LETTER CA -> 'c' */
+ { 0x9C, 0x6A }, /* BENGALI LETTER JA -> 'j' */
+ { 0xA4, 0x74 }, /* BENGALI LETTER TA -> 't' */
+ { 0xA6, 0x64 }, /* BENGALI LETTER DA -> 'd' */
+ { 0xA8, 0x6E }, /* BENGALI LETTER NA -> 'n' */
+ { 0xAA, 0x70 }, /* BENGALI LETTER PA -> 'p' */
+ { 0xAC, 0x62 }, /* BENGALI LETTER BA -> 'b' */
+ { 0xAE, 0x6D }, /* BENGALI LETTER MA -> 'm' */
+ { 0xAF, 0x79 }, /* BENGALI LETTER YA -> 'y' */
+ { 0xB0, 0x72 }, /* BENGALI LETTER RA -> 'r' */
+ { 0xB2, 0x6C }, /* BENGALI LETTER LA -> 'l' */
+ { 0xB8, 0x73 }, /* BENGALI LETTER SA -> 's' */
+ { 0xB9, 0x68 }, /* BENGALI LETTER HA -> 'h' */
+ { 0xBC, 0x27 }, /* BENGALI SIGN NUKTA -> ''' */
+ { 0xBF, 0x69 }, /* BENGALI VOWEL SIGN I -> 'i' */
+ { 0xC1, 0x75 }, /* BENGALI VOWEL SIGN U -> 'u' */
+ { 0xC3, 0x52 }, /* BENGALI VOWEL SIGN VOCALIC R -> 'R' */
+ { 0xC7, 0x65 }, /* BENGALI VOWEL SIGN E -> 'e' */
+ { 0xCB, 0x6F }, /* BENGALI VOWEL SIGN O -> 'o' */
+ { 0xD7, 0x2B }, /* BENGALI AU LENGTH MARK -> '+' */
+ { 0xE2, 0x4C }, /* BENGALI VOWEL SIGN VOCALIC L -> 'L' */
+ { 0xE6, 0x30 }, /* BENGALI DIGIT ZERO -> '0' */
+ { 0xE7, 0x31 }, /* BENGALI DIGIT ONE -> '1' */
+ { 0xE8, 0x32 }, /* BENGALI DIGIT TWO -> '2' */
+ { 0xE9, 0x33 }, /* BENGALI DIGIT THREE -> '3' */
+ { 0xEA, 0x34 }, /* BENGALI DIGIT FOUR -> '4' */
+ { 0xEB, 0x35 }, /* BENGALI DIGIT FIVE -> '5' */
+ { 0xEC, 0x36 }, /* BENGALI DIGIT SIX -> '6' */
+ { 0xED, 0x37 }, /* BENGALI DIGIT SEVEN -> '7' */
+ { 0xEE, 0x38 }, /* BENGALI DIGIT EIGHT -> '8' */
+ { 0xEF, 0x39 }, /* BENGALI DIGIT NINE -> '9' */
+ /* Entries for page 0x0A */
+ { 0x02, 0x4E }, /* GURMUKHI SIGN BINDI -> 'N' */
+ { 0x05, 0x61 }, /* GURMUKHI LETTER A -> 'a' */
+ { 0x07, 0x69 }, /* GURMUKHI LETTER I -> 'i' */
+ { 0x09, 0x75 }, /* GURMUKHI LETTER U -> 'u' */
+ { 0x15, 0x6B }, /* GURMUKHI LETTER KA -> 'k' */
+ { 0x17, 0x67 }, /* GURMUKHI LETTER GA -> 'g' */
+ { 0x1A, 0x63 }, /* GURMUKHI LETTER CA -> 'c' */
+ { 0x1C, 0x6A }, /* GURMUKHI LETTER JA -> 'j' */
+ { 0x24, 0x74 }, /* GURMUKHI LETTER TA -> 't' */
+ { 0x26, 0x64 }, /* GURMUKHI LETTER DA -> 'd' */
+ { 0x28, 0x6E }, /* GURMUKHI LETTER NA -> 'n' */
+ { 0x2A, 0x70 }, /* GURMUKHI LETTER PA -> 'p' */
+ { 0x2C, 0x62 }, /* GURMUKHI LETTER BA -> 'b' */
+ { 0x2E, 0x6D }, /* GURMUKHI LETTER MA -> 'm' */
+ { 0x2F, 0x79 }, /* GURMUKHI LETTER YA -> 'y' */
+ { 0x30, 0x72 }, /* GURMUKHI LETTER RA -> 'r' */
+ { 0x32, 0x6C }, /* GURMUKHI LETTER LA -> 'l' */
+ { 0x35, 0x76 }, /* GURMUKHI LETTER VA -> 'v' */
+ { 0x38, 0x73 }, /* GURMUKHI LETTER SA -> 's' */
+ { 0x39, 0x68 }, /* GURMUKHI LETTER HA -> 'h' */
+ { 0x3C, 0x27 }, /* GURMUKHI SIGN NUKTA -> ''' */
+ { 0x3F, 0x69 }, /* GURMUKHI VOWEL SIGN I -> 'i' */
+ { 0x41, 0x75 }, /* GURMUKHI VOWEL SIGN U -> 'u' */
+ { 0x5B, 0x7A }, /* GURMUKHI LETTER ZA -> 'z' */
+ { 0x5E, 0x66 }, /* GURMUKHI LETTER FA -> 'f' */
+ { 0x66, 0x30 }, /* GURMUKHI DIGIT ZERO -> '0' */
+ { 0x67, 0x31 }, /* GURMUKHI DIGIT ONE -> '1' */
+ { 0x68, 0x32 }, /* GURMUKHI DIGIT TWO -> '2' */
+ { 0x69, 0x33 }, /* GURMUKHI DIGIT THREE -> '3' */
+ { 0x6A, 0x34 }, /* GURMUKHI DIGIT FOUR -> '4' */
+ { 0x6B, 0x35 }, /* GURMUKHI DIGIT FIVE -> '5' */
+ { 0x6C, 0x36 }, /* GURMUKHI DIGIT SIX -> '6' */
+ { 0x6D, 0x37 }, /* GURMUKHI DIGIT SEVEN -> '7' */
+ { 0x6E, 0x38 }, /* GURMUKHI DIGIT EIGHT -> '8' */
+ { 0x6F, 0x39 }, /* GURMUKHI DIGIT NINE -> '9' */
+ { 0x70, 0x4E }, /* GURMUKHI TIPPI -> 'N' */
+ { 0x71, 0x48 }, /* GURMUKHI ADDAK -> 'H' */
+ { 0x81, 0x4E }, /* GUJARATI SIGN CANDRABINDU -> 'N' */
+ { 0x82, 0x4E }, /* GUJARATI SIGN ANUSVARA -> 'N' */
+ { 0x83, 0x48 }, /* GUJARATI SIGN VISARGA -> 'H' */
+ { 0x85, 0x61 }, /* GUJARATI LETTER A -> 'a' */
+ { 0x87, 0x69 }, /* GUJARATI LETTER I -> 'i' */
+ { 0x89, 0x75 }, /* GUJARATI LETTER U -> 'u' */
+ { 0x8B, 0x52 }, /* GUJARATI LETTER VOCALIC R -> 'R' */
+ { 0x8F, 0x65 }, /* GUJARATI LETTER E -> 'e' */
+ { 0x93, 0x6F }, /* GUJARATI LETTER O -> 'o' */
+ { 0x95, 0x6B }, /* GUJARATI LETTER KA -> 'k' */
+ { 0x97, 0x67 }, /* GUJARATI LETTER GA -> 'g' */
+ { 0x9A, 0x63 }, /* GUJARATI LETTER CA -> 'c' */
+ { 0x9C, 0x6A }, /* GUJARATI LETTER JA -> 'j' */
+ { 0xA4, 0x74 }, /* GUJARATI LETTER TA -> 't' */
+ { 0xA6, 0x64 }, /* GUJARATI LETTER DA -> 'd' */
+ { 0xA8, 0x6E }, /* GUJARATI LETTER NA -> 'n' */
+ { 0xAA, 0x70 }, /* GUJARATI LETTER PA -> 'p' */
+ { 0xAC, 0x62 }, /* GUJARATI LETTER BA -> 'b' */
+ { 0xAE, 0x6D }, /* GUJARATI LETTER MA -> 'm' */
+ { 0xB0, 0x72 }, /* GUJARATI LETTER RA -> 'r' */
+ { 0xB2, 0x6C }, /* GUJARATI LETTER LA -> 'l' */
+ { 0xB5, 0x76 }, /* GUJARATI LETTER VA -> 'v' */
+ { 0xB8, 0x73 }, /* GUJARATI LETTER SA -> 's' */
+ { 0xB9, 0x68 }, /* GUJARATI LETTER HA -> 'h' */
+ { 0xBC, 0x27 }, /* GUJARATI SIGN NUKTA -> ''' */
+ { 0xBD, 0x27 }, /* GUJARATI SIGN AVAGRAHA -> ''' */
+ { 0xBF, 0x69 }, /* GUJARATI VOWEL SIGN I -> 'i' */
+ { 0xC1, 0x75 }, /* GUJARATI VOWEL SIGN U -> 'u' */
+ { 0xC3, 0x52 }, /* GUJARATI VOWEL SIGN VOCALIC R -> 'R' */
+ { 0xC7, 0x65 }, /* GUJARATI VOWEL SIGN E -> 'e' */
+ { 0xCB, 0x6F }, /* GUJARATI VOWEL SIGN O -> 'o' */
+ { 0xE6, 0x30 }, /* GUJARATI DIGIT ZERO -> '0' */
+ { 0xE7, 0x31 }, /* GUJARATI DIGIT ONE -> '1' */
+ { 0xE8, 0x32 }, /* GUJARATI DIGIT TWO -> '2' */
+ { 0xE9, 0x33 }, /* GUJARATI DIGIT THREE -> '3' */
+ { 0xEA, 0x34 }, /* GUJARATI DIGIT FOUR -> '4' */
+ { 0xEB, 0x35 }, /* GUJARATI DIGIT FIVE -> '5' */
+ { 0xEC, 0x36 }, /* GUJARATI DIGIT SIX -> '6' */
+ { 0xED, 0x37 }, /* GUJARATI DIGIT SEVEN -> '7' */
+ { 0xEE, 0x38 }, /* GUJARATI DIGIT EIGHT -> '8' */
+ { 0xEF, 0x39 }, /* GUJARATI DIGIT NINE -> '9' */
+ /* Entries for page 0x0B */
+ { 0x01, 0x4E }, /* ORIYA SIGN CANDRABINDU -> 'N' */
+ { 0x02, 0x4E }, /* ORIYA SIGN ANUSVARA -> 'N' */
+ { 0x03, 0x48 }, /* ORIYA SIGN VISARGA -> 'H' */
+ { 0x05, 0x61 }, /* ORIYA LETTER A -> 'a' */
+ { 0x07, 0x69 }, /* ORIYA LETTER I -> 'i' */
+ { 0x09, 0x75 }, /* ORIYA LETTER U -> 'u' */
+ { 0x0B, 0x52 }, /* ORIYA LETTER VOCALIC R -> 'R' */
+ { 0x0C, 0x4C }, /* ORIYA LETTER VOCALIC L -> 'L' */
+ { 0x0F, 0x65 }, /* ORIYA LETTER E -> 'e' */
+ { 0x13, 0x6F }, /* ORIYA LETTER O -> 'o' */
+ { 0x15, 0x6B }, /* ORIYA LETTER KA -> 'k' */
+ { 0x17, 0x67 }, /* ORIYA LETTER GA -> 'g' */
+ { 0x1A, 0x63 }, /* ORIYA LETTER CA -> 'c' */
+ { 0x1C, 0x6A }, /* ORIYA LETTER JA -> 'j' */
+ { 0x24, 0x74 }, /* ORIYA LETTER TA -> 't' */
+ { 0x26, 0x64 }, /* ORIYA LETTER DA -> 'd' */
+ { 0x28, 0x6E }, /* ORIYA LETTER NA -> 'n' */
+ { 0x2A, 0x70 }, /* ORIYA LETTER PA -> 'p' */
+ { 0x2C, 0x62 }, /* ORIYA LETTER BA -> 'b' */
+ { 0x2E, 0x6D }, /* ORIYA LETTER MA -> 'm' */
+ { 0x2F, 0x79 }, /* ORIYA LETTER YA -> 'y' */
+ { 0x30, 0x72 }, /* ORIYA LETTER RA -> 'r' */
+ { 0x32, 0x6C }, /* ORIYA LETTER LA -> 'l' */
+ { 0x38, 0x73 }, /* ORIYA LETTER SA -> 's' */
+ { 0x39, 0x68 }, /* ORIYA LETTER HA -> 'h' */
+ { 0x3C, 0x27 }, /* ORIYA SIGN NUKTA -> ''' */
+ { 0x3D, 0x27 }, /* ORIYA SIGN AVAGRAHA -> ''' */
+ { 0x3F, 0x69 }, /* ORIYA VOWEL SIGN I -> 'i' */
+ { 0x41, 0x75 }, /* ORIYA VOWEL SIGN U -> 'u' */
+ { 0x43, 0x52 }, /* ORIYA VOWEL SIGN VOCALIC R -> 'R' */
+ { 0x47, 0x65 }, /* ORIYA VOWEL SIGN E -> 'e' */
+ { 0x4B, 0x6F }, /* ORIYA VOWEL SIGN O -> 'o' */
+ { 0x56, 0x2B }, /* ORIYA AI LENGTH MARK -> '+' */
+ { 0x57, 0x2B }, /* ORIYA AU LENGTH MARK -> '+' */
+ { 0x66, 0x30 }, /* ORIYA DIGIT ZERO -> '0' */
+ { 0x67, 0x31 }, /* ORIYA DIGIT ONE -> '1' */
+ { 0x68, 0x32 }, /* ORIYA DIGIT TWO -> '2' */
+ { 0x69, 0x33 }, /* ORIYA DIGIT THREE -> '3' */
+ { 0x6A, 0x34 }, /* ORIYA DIGIT FOUR -> '4' */
+ { 0x6B, 0x35 }, /* ORIYA DIGIT FIVE -> '5' */
+ { 0x6C, 0x36 }, /* ORIYA DIGIT SIX -> '6' */
+ { 0x6D, 0x37 }, /* ORIYA DIGIT SEVEN -> '7' */
+ { 0x6E, 0x38 }, /* ORIYA DIGIT EIGHT -> '8' */
+ { 0x6F, 0x39 }, /* ORIYA DIGIT NINE -> '9' */
+ { 0x82, 0x4E }, /* TAMIL SIGN ANUSVARA -> 'N' */
+ { 0x83, 0x48 }, /* TAMIL SIGN VISARGA -> 'H' */
+ { 0x85, 0x61 }, /* TAMIL LETTER A -> 'a' */
+ { 0x87, 0x69 }, /* TAMIL LETTER I -> 'i' */
+ { 0x89, 0x75 }, /* TAMIL LETTER U -> 'u' */
+ { 0x8E, 0x65 }, /* TAMIL LETTER E -> 'e' */
+ { 0x92, 0x6F }, /* TAMIL LETTER O -> 'o' */
+ { 0x95, 0x6B }, /* TAMIL LETTER KA -> 'k' */
+ { 0x9A, 0x63 }, /* TAMIL LETTER CA -> 'c' */
+ { 0x9C, 0x6A }, /* TAMIL LETTER JA -> 'j' */
+ { 0xA4, 0x74 }, /* TAMIL LETTER TA -> 't' */
+ { 0xA8, 0x6E }, /* TAMIL LETTER NA -> 'n' */
+ { 0xAA, 0x70 }, /* TAMIL LETTER PA -> 'p' */
+ { 0xAE, 0x6D }, /* TAMIL LETTER MA -> 'm' */
+ { 0xAF, 0x79 }, /* TAMIL LETTER YA -> 'y' */
+ { 0xB0, 0x72 }, /* TAMIL LETTER RA -> 'r' */
+ { 0xB2, 0x6C }, /* TAMIL LETTER LA -> 'l' */
+ { 0xB5, 0x76 }, /* TAMIL LETTER VA -> 'v' */
+ { 0xB8, 0x73 }, /* TAMIL LETTER SA -> 's' */
+ { 0xB9, 0x68 }, /* TAMIL LETTER HA -> 'h' */
+ { 0xBF, 0x69 }, /* TAMIL VOWEL SIGN I -> 'i' */
+ { 0xC1, 0x75 }, /* TAMIL VOWEL SIGN U -> 'u' */
+ { 0xC6, 0x65 }, /* TAMIL VOWEL SIGN E -> 'e' */
+ { 0xCA, 0x6F }, /* TAMIL VOWEL SIGN O -> 'o' */
+ { 0xD7, 0x2B }, /* TAMIL AU LENGTH MARK -> '+' */
+ { 0xE6, 0x30 }, /* TAMIL DIGIT ZERO -> '0' */
+ { 0xE7, 0x31 }, /* TAMIL DIGIT ONE -> '1' */
+ { 0xE8, 0x32 }, /* TAMIL DIGIT TWO -> '2' */
+ { 0xE9, 0x33 }, /* TAMIL DIGIT THREE -> '3' */
+ { 0xEA, 0x34 }, /* TAMIL DIGIT FOUR -> '4' */
+ { 0xEB, 0x35 }, /* TAMIL DIGIT FIVE -> '5' */
+ { 0xEC, 0x36 }, /* TAMIL DIGIT SIX -> '6' */
+ { 0xED, 0x37 }, /* TAMIL DIGIT SEVEN -> '7' */
+ { 0xEE, 0x38 }, /* TAMIL DIGIT EIGHT -> '8' */
+ { 0xEF, 0x39 }, /* TAMIL DIGIT NINE -> '9' */
+ /* Entries for page 0x0C */
+ { 0x01, 0x4E }, /* TELUGU SIGN CANDRABINDU -> 'N' */
+ { 0x02, 0x4E }, /* TELUGU SIGN ANUSVARA -> 'N' */
+ { 0x03, 0x48 }, /* TELUGU SIGN VISARGA -> 'H' */
+ { 0x05, 0x61 }, /* TELUGU LETTER A -> 'a' */
+ { 0x07, 0x69 }, /* TELUGU LETTER I -> 'i' */
+ { 0x09, 0x75 }, /* TELUGU LETTER U -> 'u' */
+ { 0x0B, 0x52 }, /* TELUGU LETTER VOCALIC R -> 'R' */
+ { 0x0C, 0x4C }, /* TELUGU LETTER VOCALIC L -> 'L' */
+ { 0x0E, 0x65 }, /* TELUGU LETTER E -> 'e' */
+ { 0x12, 0x6F }, /* TELUGU LETTER O -> 'o' */
+ { 0x15, 0x6B }, /* TELUGU LETTER KA -> 'k' */
+ { 0x17, 0x67 }, /* TELUGU LETTER GA -> 'g' */
+ { 0x1A, 0x63 }, /* TELUGU LETTER CA -> 'c' */
+ { 0x1C, 0x6A }, /* TELUGU LETTER JA -> 'j' */
+ { 0x24, 0x74 }, /* TELUGU LETTER TA -> 't' */
+ { 0x26, 0x64 }, /* TELUGU LETTER DA -> 'd' */
+ { 0x28, 0x6E }, /* TELUGU LETTER NA -> 'n' */
+ { 0x2A, 0x70 }, /* TELUGU LETTER PA -> 'p' */
+ { 0x2C, 0x62 }, /* TELUGU LETTER BA -> 'b' */
+ { 0x2E, 0x6D }, /* TELUGU LETTER MA -> 'm' */
+ { 0x2F, 0x79 }, /* TELUGU LETTER YA -> 'y' */
+ { 0x30, 0x72 }, /* TELUGU LETTER RA -> 'r' */
+ { 0x32, 0x6C }, /* TELUGU LETTER LA -> 'l' */
+ { 0x35, 0x76 }, /* TELUGU LETTER VA -> 'v' */
+ { 0x38, 0x73 }, /* TELUGU LETTER SA -> 's' */
+ { 0x39, 0x68 }, /* TELUGU LETTER HA -> 'h' */
+ { 0x3F, 0x69 }, /* TELUGU VOWEL SIGN I -> 'i' */
+ { 0x41, 0x75 }, /* TELUGU VOWEL SIGN U -> 'u' */
+ { 0x43, 0x52 }, /* TELUGU VOWEL SIGN VOCALIC R -> 'R' */
+ { 0x46, 0x65 }, /* TELUGU VOWEL SIGN E -> 'e' */
+ { 0x4A, 0x6F }, /* TELUGU VOWEL SIGN O -> 'o' */
+ { 0x55, 0x2B }, /* TELUGU LENGTH MARK -> '+' */
+ { 0x56, 0x2B }, /* TELUGU AI LENGTH MARK -> '+' */
+ { 0x66, 0x30 }, /* TELUGU DIGIT ZERO -> '0' */
+ { 0x67, 0x31 }, /* TELUGU DIGIT ONE -> '1' */
+ { 0x68, 0x32 }, /* TELUGU DIGIT TWO -> '2' */
+ { 0x69, 0x33 }, /* TELUGU DIGIT THREE -> '3' */
+ { 0x6A, 0x34 }, /* TELUGU DIGIT FOUR -> '4' */
+ { 0x6B, 0x35 }, /* TELUGU DIGIT FIVE -> '5' */
+ { 0x6C, 0x36 }, /* TELUGU DIGIT SIX -> '6' */
+ { 0x6D, 0x37 }, /* TELUGU DIGIT SEVEN -> '7' */
+ { 0x6E, 0x38 }, /* TELUGU DIGIT EIGHT -> '8' */
+ { 0x6F, 0x39 }, /* TELUGU DIGIT NINE -> '9' */
+ { 0x82, 0x4E }, /* KANNADA SIGN ANUSVARA -> 'N' */
+ { 0x83, 0x48 }, /* KANNADA SIGN VISARGA -> 'H' */
+ { 0x85, 0x61 }, /* KANNADA LETTER A -> 'a' */
+ { 0x87, 0x69 }, /* KANNADA LETTER I -> 'i' */
+ { 0x89, 0x75 }, /* KANNADA LETTER U -> 'u' */
+ { 0x8B, 0x52 }, /* KANNADA LETTER VOCALIC R -> 'R' */
+ { 0x8C, 0x4C }, /* KANNADA LETTER VOCALIC L -> 'L' */
+ { 0x8E, 0x65 }, /* KANNADA LETTER E -> 'e' */
+ { 0x92, 0x6F }, /* KANNADA LETTER O -> 'o' */
+ { 0x95, 0x6B }, /* KANNADA LETTER KA -> 'k' */
+ { 0x97, 0x67 }, /* KANNADA LETTER GA -> 'g' */
+ { 0x9A, 0x63 }, /* KANNADA LETTER CA -> 'c' */
+ { 0x9C, 0x6A }, /* KANNADA LETTER JA -> 'j' */
+ { 0xA4, 0x74 }, /* KANNADA LETTER TA -> 't' */
+ { 0xA6, 0x64 }, /* KANNADA LETTER DA -> 'd' */
+ { 0xA8, 0x6E }, /* KANNADA LETTER NA -> 'n' */
+ { 0xAA, 0x70 }, /* KANNADA LETTER PA -> 'p' */
+ { 0xAC, 0x62 }, /* KANNADA LETTER BA -> 'b' */
+ { 0xAE, 0x6D }, /* KANNADA LETTER MA -> 'm' */
+ { 0xAF, 0x79 }, /* KANNADA LETTER YA -> 'y' */
+ { 0xB0, 0x72 }, /* KANNADA LETTER RA -> 'r' */
+ { 0xB2, 0x6C }, /* KANNADA LETTER LA -> 'l' */
+ { 0xB5, 0x76 }, /* KANNADA LETTER VA -> 'v' */
+ { 0xB8, 0x73 }, /* KANNADA LETTER SA -> 's' */
+ { 0xB9, 0x68 }, /* KANNADA LETTER HA -> 'h' */
+ { 0xBF, 0x69 }, /* KANNADA VOWEL SIGN I -> 'i' */
+ { 0xC1, 0x75 }, /* KANNADA VOWEL SIGN U -> 'u' */
+ { 0xC3, 0x52 }, /* KANNADA VOWEL SIGN VOCALIC R -> 'R' */
+ { 0xC6, 0x65 }, /* KANNADA VOWEL SIGN E -> 'e' */
+ { 0xCA, 0x6F }, /* KANNADA VOWEL SIGN O -> 'o' */
+ { 0xD5, 0x2B }, /* KANNADA LENGTH MARK -> '+' */
+ { 0xD6, 0x2B }, /* KANNADA AI LENGTH MARK -> '+' */
+ { 0xE6, 0x30 }, /* KANNADA DIGIT ZERO -> '0' */
+ { 0xE7, 0x31 }, /* KANNADA DIGIT ONE -> '1' */
+ { 0xE8, 0x32 }, /* KANNADA DIGIT TWO -> '2' */
+ { 0xE9, 0x33 }, /* KANNADA DIGIT THREE -> '3' */
+ { 0xEA, 0x34 }, /* KANNADA DIGIT FOUR -> '4' */
+ { 0xEB, 0x35 }, /* KANNADA DIGIT FIVE -> '5' */
+ { 0xEC, 0x36 }, /* KANNADA DIGIT SIX -> '6' */
+ { 0xED, 0x37 }, /* KANNADA DIGIT SEVEN -> '7' */
+ { 0xEE, 0x38 }, /* KANNADA DIGIT EIGHT -> '8' */
+ { 0xEF, 0x39 }, /* KANNADA DIGIT NINE -> '9' */
+ /* Entries for page 0x0D */
+ { 0x02, 0x4E }, /* MALAYALAM SIGN ANUSVARA -> 'N' */
+ { 0x03, 0x48 }, /* MALAYALAM SIGN VISARGA -> 'H' */
+ { 0x05, 0x61 }, /* MALAYALAM LETTER A -> 'a' */
+ { 0x07, 0x69 }, /* MALAYALAM LETTER I -> 'i' */
+ { 0x09, 0x75 }, /* MALAYALAM LETTER U -> 'u' */
+ { 0x0B, 0x52 }, /* MALAYALAM LETTER VOCALIC R -> 'R' */
+ { 0x0C, 0x4C }, /* MALAYALAM LETTER VOCALIC L -> 'L' */
+ { 0x0E, 0x65 }, /* MALAYALAM LETTER E -> 'e' */
+ { 0x12, 0x6F }, /* MALAYALAM LETTER O -> 'o' */
+ { 0x15, 0x6B }, /* MALAYALAM LETTER KA -> 'k' */
+ { 0x17, 0x67 }, /* MALAYALAM LETTER GA -> 'g' */
+ { 0x1A, 0x63 }, /* MALAYALAM LETTER CA -> 'c' */
+ { 0x1C, 0x6A }, /* MALAYALAM LETTER JA -> 'j' */
+ { 0x24, 0x74 }, /* MALAYALAM LETTER TA -> 't' */
+ { 0x26, 0x64 }, /* MALAYALAM LETTER DA -> 'd' */
+ { 0x28, 0x6E }, /* MALAYALAM LETTER NA -> 'n' */
+ { 0x2A, 0x70 }, /* MALAYALAM LETTER PA -> 'p' */
+ { 0x2C, 0x62 }, /* MALAYALAM LETTER BA -> 'b' */
+ { 0x2E, 0x6D }, /* MALAYALAM LETTER MA -> 'm' */
+ { 0x2F, 0x79 }, /* MALAYALAM LETTER YA -> 'y' */
+ { 0x30, 0x72 }, /* MALAYALAM LETTER RA -> 'r' */
+ { 0x32, 0x6C }, /* MALAYALAM LETTER LA -> 'l' */
+ { 0x35, 0x76 }, /* MALAYALAM LETTER VA -> 'v' */
+ { 0x38, 0x73 }, /* MALAYALAM LETTER SA -> 's' */
+ { 0x39, 0x68 }, /* MALAYALAM LETTER HA -> 'h' */
+ { 0x3F, 0x69 }, /* MALAYALAM VOWEL SIGN I -> 'i' */
+ { 0x41, 0x75 }, /* MALAYALAM VOWEL SIGN U -> 'u' */
+ { 0x43, 0x52 }, /* MALAYALAM VOWEL SIGN VOCALIC R -> 'R' */
+ { 0x46, 0x65 }, /* MALAYALAM VOWEL SIGN E -> 'e' */
+ { 0x4A, 0x6F }, /* MALAYALAM VOWEL SIGN O -> 'o' */
+ { 0x57, 0x2B }, /* MALAYALAM AU LENGTH MARK -> '+' */
+ { 0x66, 0x30 }, /* MALAYALAM DIGIT ZERO -> '0' */
+ { 0x67, 0x31 }, /* MALAYALAM DIGIT ONE -> '1' */
+ { 0x68, 0x32 }, /* MALAYALAM DIGIT TWO -> '2' */
+ { 0x69, 0x33 }, /* MALAYALAM DIGIT THREE -> '3' */
+ { 0x6A, 0x34 }, /* MALAYALAM DIGIT FOUR -> '4' */
+ { 0x6B, 0x35 }, /* MALAYALAM DIGIT FIVE -> '5' */
+ { 0x6C, 0x36 }, /* MALAYALAM DIGIT SIX -> '6' */
+ { 0x6D, 0x37 }, /* MALAYALAM DIGIT SEVEN -> '7' */
+ { 0x6E, 0x38 }, /* MALAYALAM DIGIT EIGHT -> '8' */
+ { 0x6F, 0x39 }, /* MALAYALAM DIGIT NINE -> '9' */
+ { 0x82, 0x4E }, /* SINHALA SIGN ANUSVARAYA -> 'N' */
+ { 0x83, 0x48 }, /* SINHALA SIGN VISARGAYA -> 'H' */
+ { 0x85, 0x61 }, /* SINHALA LETTER AYANNA -> 'a' */
+ { 0x89, 0x69 }, /* SINHALA LETTER IYANNA -> 'i' */
+ { 0x8B, 0x75 }, /* SINHALA LETTER UYANNA -> 'u' */
+ { 0x8D, 0x52 }, /* SINHALA LETTER IRUYANNA -> 'R' */
+ { 0x8F, 0x4C }, /* SINHALA LETTER ILUYANNA -> 'L' */
+ { 0x91, 0x65 }, /* SINHALA LETTER EYANNA -> 'e' */
+ { 0x94, 0x6F }, /* SINHALA LETTER OYANNA -> 'o' */
+ { 0x9A, 0x6B }, /* SINHALA LETTER ALPAPRAANA KAYANNA -> 'k' */
+ { 0x9C, 0x67 }, /* SINHALA LETTER ALPAPRAANA GAYANNA -> 'g' */
+ { 0xA0, 0x63 }, /* SINHALA LETTER ALPAPRAANA CAYANNA -> 'c' */
+ { 0xA2, 0x6A }, /* SINHALA LETTER ALPAPRAANA JAYANNA -> 'j' */
+ { 0xAD, 0x74 }, /* SINHALA LETTER ALPAPRAANA TAYANNA -> 't' */
+ { 0xAF, 0x64 }, /* SINHALA LETTER ALPAPRAANA DAYANNA -> 'd' */
+ { 0xB1, 0x6E }, /* SINHALA LETTER DANTAJA NAYANNA -> 'n' */
+ { 0xB4, 0x70 }, /* SINHALA LETTER ALPAPRAANA PAYANNA -> 'p' */
+ { 0xB6, 0x62 }, /* SINHALA LETTER ALPAPRAANA BAYANNA -> 'b' */
+ { 0xB8, 0x6D }, /* SINHALA LETTER MAYANNA -> 'm' */
+ { 0xBA, 0x79 }, /* SINHALA LETTER YAYANNA -> 'y' */
+ { 0xBB, 0x72 }, /* SINHALA LETTER RAYANNA -> 'r' */
+ { 0xBD, 0x6C }, /* SINHALA LETTER DANTAJA LAYANNA -> 'l' */
+ { 0xC0, 0x76 }, /* SINHALA LETTER VAYANNA -> 'v' */
+ { 0xC3, 0x73 }, /* SINHALA LETTER DANTAJA SAYANNA -> 's' */
+ { 0xC4, 0x68 }, /* SINHALA LETTER HAYANNA -> 'h' */
+ { 0xC6, 0x66 }, /* SINHALA LETTER FAYANNA -> 'f' */
+ { 0xD2, 0x69 }, /* SINHALA VOWEL SIGN KETTI IS-PILLA -> 'i' */
+ { 0xD4, 0x75 }, /* SINHALA VOWEL SIGN KETTI PAA-PILLA -> 'u' */
+ { 0xD8, 0x52 }, /* SINHALA VOWEL SIGN GAETTA-PILLA -> 'R' */
+ { 0xD9, 0x65 }, /* SINHALA VOWEL SIGN KOMBUVA -> 'e' */
+ { 0xDC, 0x6F }, /* SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA -> 'o' */
+ { 0xDF, 0x4C }, /* SINHALA VOWEL SIGN GAYANUKITTA -> 'L' */
+ /* Entries for page 0x0E */
+ { 0x01, 0x6B }, /* THAI CHARACTER KO KAI -> 'k' */
+ { 0x0D, 0x79 }, /* THAI CHARACTER YO YING -> 'y' */
+ { 0x0E, 0x64 }, /* THAI CHARACTER DO CHADA -> 'd' */
+ { 0x0F, 0x74 }, /* THAI CHARACTER TO PATAK -> 't' */
+ { 0x13, 0x6E }, /* THAI CHARACTER NO NEN -> 'n' */
+ { 0x14, 0x64 }, /* THAI CHARACTER DO DEK -> 'd' */
+ { 0x15, 0x74 }, /* THAI CHARACTER TO TAO -> 't' */
+ { 0x19, 0x6E }, /* THAI CHARACTER NO NU -> 'n' */
+ { 0x1A, 0x62 }, /* THAI CHARACTER BO BAIMAI -> 'b' */
+ { 0x1B, 0x70 }, /* THAI CHARACTER PO PLA -> 'p' */
+ { 0x1D, 0x66 }, /* THAI CHARACTER FO FA -> 'f' */
+ { 0x1F, 0x66 }, /* THAI CHARACTER FO FAN -> 'f' */
+ { 0x21, 0x6D }, /* THAI CHARACTER MO MA -> 'm' */
+ { 0x22, 0x79 }, /* THAI CHARACTER YO YAK -> 'y' */
+ { 0x23, 0x72 }, /* THAI CHARACTER RO RUA -> 'r' */
+ { 0x24, 0x52 }, /* THAI CHARACTER RU -> 'R' */
+ { 0x25, 0x6C }, /* THAI CHARACTER LO LING -> 'l' */
+ { 0x26, 0x4C }, /* THAI CHARACTER LU -> 'L' */
+ { 0x27, 0x77 }, /* THAI CHARACTER WO WAEN -> 'w' */
+ { 0x28, 0x00 }, /* THAI CHARACTER SO SALA -> ... */
+ { 0x2A, 0x73 }, /* THAI CHARACTER SO SUA -> 's' */
+ { 0x2B, 0x68 }, /* THAI CHARACTER HO HIP -> 'h' */
+ { 0x2C, 0x6C }, /* THAI CHARACTER LO CHULA -> 'l' */
+ { 0x2D, 0x60 }, /* THAI CHARACTER O ANG -> '`' */
+ { 0x2E, 0x68 }, /* THAI CHARACTER HO NOKHUK -> 'h' */
+ { 0x2F, 0x7E }, /* THAI CHARACTER PAIYANNOI -> '~' */
+ { 0x30, 0x61 }, /* THAI CHARACTER SARA A -> 'a' */
+ { 0x31, 0x61 }, /* THAI CHARACTER MAI HAN-AKAT -> 'a' */
+ { 0x34, 0x69 }, /* THAI CHARACTER SARA I -> 'i' */
+ { 0x38, 0x75 }, /* THAI CHARACTER SARA U -> 'u' */
+ { 0x3A, 0x27 }, /* THAI CHARACTER PHINTHU -> ''' */
+ { 0x40, 0x65 }, /* THAI CHARACTER SARA E -> 'e' */
+ { 0x42, 0x6F }, /* THAI CHARACTER SARA O -> 'o' */
+ { 0x46, 0x2B }, /* THAI CHARACTER MAIYAMOK -> '+' */
+ { 0x4D, 0x4D }, /* THAI CHARACTER NIKHAHIT -> 'M' */
+ { 0x50, 0x30 }, /* THAI DIGIT ZERO -> '0' */
+ { 0x51, 0x31 }, /* THAI DIGIT ONE -> '1' */
+ { 0x52, 0x32 }, /* THAI DIGIT TWO -> '2' */
+ { 0x53, 0x33 }, /* THAI DIGIT THREE -> '3' */
+ { 0x54, 0x34 }, /* THAI DIGIT FOUR -> '4' */
+ { 0x55, 0x35 }, /* THAI DIGIT FIVE -> '5' */
+ { 0x56, 0x36 }, /* THAI DIGIT SIX -> '6' */
+ { 0x57, 0x37 }, /* THAI DIGIT SEVEN -> '7' */
+ { 0x58, 0x38 }, /* THAI DIGIT EIGHT -> '8' */
+ { 0x59, 0x39 }, /* THAI DIGIT NINE -> '9' */
+ { 0x81, 0x6B }, /* LAO LETTER KO -> 'k' */
+ { 0x8A, 0x73 }, /* LAO LETTER SO TAM -> 's' */
+ { 0x94, 0x64 }, /* LAO LETTER DO -> 'd' */
+ { 0x95, 0x68 }, /* LAO LETTER TO -> 'h' */
+ { 0x99, 0x6E }, /* LAO LETTER NO -> 'n' */
+ { 0x9A, 0x62 }, /* LAO LETTER BO -> 'b' */
+ { 0x9B, 0x70 }, /* LAO LETTER PO -> 'p' */
+ { 0x9D, 0x66 }, /* LAO LETTER FO TAM -> 'f' */
+ { 0x9F, 0x66 }, /* LAO LETTER FO SUNG -> 'f' */
+ { 0xA1, 0x6D }, /* LAO LETTER MO -> 'm' */
+ { 0xA2, 0x79 }, /* LAO LETTER YO -> 'y' */
+ { 0xA3, 0x72 }, /* LAO LETTER LO LING -> 'r' */
+ { 0xA5, 0x6C }, /* LAO LETTER LO LOOT -> 'l' */
+ { 0xA7, 0x77 }, /* LAO LETTER WO -> 'w' */
+ { 0xAA, 0x73 }, /* LAO LETTER SO SUNG -> 's' */
+ { 0xAB, 0x68 }, /* LAO LETTER HO SUNG -> 'h' */
+ { 0xAD, 0x60 }, /* LAO LETTER O -> '`' */
+ { 0xAF, 0x7E }, /* LAO ELLIPSIS -> '~' */
+ { 0xB0, 0x61 }, /* LAO VOWEL SIGN A -> 'a' */
+ { 0xB4, 0x69 }, /* LAO VOWEL SIGN I -> 'i' */
+ { 0xB6, 0x79 }, /* LAO VOWEL SIGN Y -> 'y' */
+ { 0xB8, 0x75 }, /* LAO VOWEL SIGN U -> 'u' */
+ { 0xBB, 0x6F }, /* LAO VOWEL SIGN MAI KON -> 'o' */
+ { 0xBC, 0x6C }, /* LAO SEMIVOWEL SIGN LO -> 'l' */
+ { 0xC0, 0x65 }, /* LAO VOWEL SIGN E -> 'e' */
+ { 0xC2, 0x6F }, /* LAO VOWEL SIGN O -> 'o' */
+ { 0xC6, 0x2B }, /* LAO KO LA -> '+' */
+ { 0xCD, 0x4D }, /* LAO NIGGAHITA -> 'M' */
+ { 0xD0, 0x30 }, /* LAO DIGIT ZERO -> '0' */
+ { 0xD1, 0x31 }, /* LAO DIGIT ONE -> '1' */
+ { 0xD2, 0x32 }, /* LAO DIGIT TWO -> '2' */
+ { 0xD3, 0x33 }, /* LAO DIGIT THREE -> '3' */
+ { 0xD4, 0x34 }, /* LAO DIGIT FOUR -> '4' */
+ { 0xD5, 0x35 }, /* LAO DIGIT FIVE -> '5' */
+ { 0xD6, 0x36 }, /* LAO DIGIT SIX -> '6' */
+ { 0xD7, 0x37 }, /* LAO DIGIT SEVEN -> '7' */
+ { 0xD8, 0x38 }, /* LAO DIGIT EIGHT -> '8' */
+ { 0xD9, 0x39 }, /* LAO DIGIT NINE -> '9' */
+ /* Entries for page 0x0F */
+ { 0x0B, 0x2D }, /* TIBETAN MARK INTERSYLLABIC TSHEG -> '-' */
+ { 0x20, 0x30 }, /* TIBETAN DIGIT ZERO -> '0' */
+ { 0x21, 0x31 }, /* TIBETAN DIGIT ONE -> '1' */
+ { 0x22, 0x32 }, /* TIBETAN DIGIT TWO -> '2' */
+ { 0x23, 0x33 }, /* TIBETAN DIGIT THREE -> '3' */
+ { 0x24, 0x34 }, /* TIBETAN DIGIT FOUR -> '4' */
+ { 0x25, 0x35 }, /* TIBETAN DIGIT FIVE -> '5' */
+ { 0x26, 0x36 }, /* TIBETAN DIGIT SIX -> '6' */
+ { 0x27, 0x37 }, /* TIBETAN DIGIT SEVEN -> '7' */
+ { 0x28, 0x38 }, /* TIBETAN DIGIT EIGHT -> '8' */
+ { 0x29, 0x39 }, /* TIBETAN DIGIT NINE -> '9' */
+ { 0x34, 0x2B }, /* TIBETAN MARK BSDUS RTAGS -> '+' */
+ { 0x35, 0x2A }, /* TIBETAN MARK NGAS BZUNG NYI ZLA -> '*' */
+ { 0x36, 0x5E }, /* TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN -> '^' */
+ { 0x37, 0x5F }, /* TIBETAN MARK NGAS BZUNG SGOR RTAGS -> '_' */
+ { 0x39, 0x7E }, /* TIBETAN MARK TSA -PHRU -> '~' */
+ { 0x3B, 0x5D }, /* TIBETAN MARK GUG RTAGS GYAS -> ']' */
+ { 0x40, 0x6B }, /* TIBETAN LETTER KA -> 'k' */
+ { 0x42, 0x67 }, /* TIBETAN LETTER GA -> 'g' */
+ { 0x45, 0x63 }, /* TIBETAN LETTER CA -> 'c' */
+ { 0x47, 0x6A }, /* TIBETAN LETTER JA -> 'j' */
+ { 0x4F, 0x74 }, /* TIBETAN LETTER TA -> 't' */
+ { 0x51, 0x64 }, /* TIBETAN LETTER DA -> 'd' */
+ { 0x53, 0x6E }, /* TIBETAN LETTER NA -> 'n' */
+ { 0x54, 0x70 }, /* TIBETAN LETTER PA -> 'p' */
+ { 0x56, 0x62 }, /* TIBETAN LETTER BA -> 'b' */
+ { 0x58, 0x6D }, /* TIBETAN LETTER MA -> 'm' */
+ { 0x5D, 0x77 }, /* TIBETAN LETTER WA -> 'w' */
+ { 0x5F, 0x7A }, /* TIBETAN LETTER ZA -> 'z' */
+ { 0x60, 0x27 }, /* TIBETAN LETTER -A -> ''' */
+ { 0x61, 0x79 }, /* TIBETAN LETTER YA -> 'y' */
+ { 0x62, 0x72 }, /* TIBETAN LETTER RA -> 'r' */
+ { 0x63, 0x6C }, /* TIBETAN LETTER LA -> 'l' */
+ { 0x66, 0x73 }, /* TIBETAN LETTER SA -> 's' */
+ { 0x67, 0x68 }, /* TIBETAN LETTER HA -> 'h' */
+ { 0x68, 0x61 }, /* TIBETAN LETTER A -> 'a' */
+ { 0x6A, 0x72 }, /* TIBETAN LETTER FIXED-FORM RA -> 'r' */
+ { 0x72, 0x69 }, /* TIBETAN VOWEL SIGN I -> 'i' */
+ { 0x74, 0x75 }, /* TIBETAN VOWEL SIGN U -> 'u' */
+ { 0x76, 0x52 }, /* TIBETAN VOWEL SIGN VOCALIC R -> 'R' */
+ { 0x78, 0x4C }, /* TIBETAN VOWEL SIGN VOCALIC L -> 'L' */
+ { 0x7A, 0x65 }, /* TIBETAN VOWEL SIGN E -> 'e' */
+ { 0x7C, 0x6F }, /* TIBETAN VOWEL SIGN O -> 'o' */
+ { 0x7E, 0x4D }, /* TIBETAN SIGN RJES SU NGA RO -> 'M' */
+ { 0x7F, 0x48 }, /* TIBETAN SIGN RNAM BCAD -> 'H' */
+ { 0x80, 0x69 }, /* TIBETAN VOWEL SIGN REVERSED I -> 'i' */
+ { 0x90, 0x6B }, /* TIBETAN SUBJOINED LETTER KA -> 'k' */
+ { 0x92, 0x67 }, /* TIBETAN SUBJOINED LETTER GA -> 'g' */
+ { 0x95, 0x63 }, /* TIBETAN SUBJOINED LETTER CA -> 'c' */
+ { 0x97, 0x6A }, /* TIBETAN SUBJOINED LETTER JA -> 'j' */
+ { 0x9F, 0x74 }, /* TIBETAN SUBJOINED LETTER TA -> 't' */
+ { 0xA1, 0x64 }, /* TIBETAN SUBJOINED LETTER DA -> 'd' */
+ { 0xA3, 0x6E }, /* TIBETAN SUBJOINED LETTER NA -> 'n' */
+ { 0xA4, 0x70 }, /* TIBETAN SUBJOINED LETTER PA -> 'p' */
+ { 0xA6, 0x62 }, /* TIBETAN SUBJOINED LETTER BA -> 'b' */
+ { 0xA8, 0x6D }, /* TIBETAN SUBJOINED LETTER MA -> 'm' */
+ { 0xAD, 0x77 }, /* TIBETAN SUBJOINED LETTER WA -> 'w' */
+ { 0xAF, 0x7A }, /* TIBETAN SUBJOINED LETTER ZA -> 'z' */
+ { 0xB0, 0x27 }, /* TIBETAN SUBJOINED LETTER -A -> ''' */
+ { 0xB1, 0x79 }, /* TIBETAN SUBJOINED LETTER YA -> 'y' */
+ { 0xB2, 0x72 }, /* TIBETAN SUBJOINED LETTER RA -> 'r' */
+ { 0xB3, 0x6C }, /* TIBETAN SUBJOINED LETTER LA -> 'l' */
+ { 0xB6, 0x73 }, /* TIBETAN SUBJOINED LETTER SA -> 's' */
+ { 0xB7, 0x68 }, /* TIBETAN SUBJOINED LETTER HA -> 'h' */
+ { 0xB8, 0x61 }, /* TIBETAN SUBJOINED LETTER A -> 'a' */
+ { 0xBA, 0x77 }, /* TIBETAN SUBJOINED LETTER FIXED-FORM WA -> 'w' */
+ { 0xBB, 0x79 }, /* TIBETAN SUBJOINED LETTER FIXED-FORM YA -> 'y' */
+ { 0xBC, 0x72 }, /* TIBETAN SUBJOINED LETTER FIXED-FORM RA -> 'r' */
+ { 0xBE, 0x58 }, /* TIBETAN KU RU KHA -> 'X' */
+ /* Entries for page 0x10 */
+ { 0x00, 0x6B }, /* MYANMAR LETTER KA -> 'k' */
+ { 0x02, 0x67 }, /* MYANMAR LETTER GA -> 'g' */
+ { 0x05, 0x63 }, /* MYANMAR LETTER CA -> 'c' */
+ { 0x07, 0x6A }, /* MYANMAR LETTER JA -> 'j' */
+ { 0x12, 0x64 }, /* MYANMAR LETTER DA -> 'd' */
+ { 0x14, 0x6E }, /* MYANMAR LETTER NA -> 'n' */
+ { 0x15, 0x70 }, /* MYANMAR LETTER PA -> 'p' */
+ { 0x17, 0x62 }, /* MYANMAR LETTER BA -> 'b' */
+ { 0x19, 0x6D }, /* MYANMAR LETTER MA -> 'm' */
+ { 0x1A, 0x79 }, /* MYANMAR LETTER YA -> 'y' */
+ { 0x1B, 0x72 }, /* MYANMAR LETTER RA -> 'r' */
+ { 0x1C, 0x6C }, /* MYANMAR LETTER LA -> 'l' */
+ { 0x1D, 0x77 }, /* MYANMAR LETTER WA -> 'w' */
+ { 0x1E, 0x73 }, /* MYANMAR LETTER SA -> 's' */
+ { 0x1F, 0x68 }, /* MYANMAR LETTER HA -> 'h' */
+ { 0x21, 0x61 }, /* MYANMAR LETTER A -> 'a' */
+ { 0x23, 0x69 }, /* MYANMAR LETTER I -> 'i' */
+ { 0x25, 0x75 }, /* MYANMAR LETTER U -> 'u' */
+ { 0x27, 0x65 }, /* MYANMAR LETTER E -> 'e' */
+ { 0x29, 0x6F }, /* MYANMAR LETTER O -> 'o' */
+ { 0x2D, 0x69 }, /* MYANMAR VOWEL SIGN I -> 'i' */
+ { 0x2F, 0x75 }, /* MYANMAR VOWEL SIGN U -> 'u' */
+ { 0x31, 0x65 }, /* MYANMAR VOWEL SIGN E -> 'e' */
+ { 0x36, 0x4E }, /* MYANMAR SIGN ANUSVARA -> 'N' */
+ { 0x37, 0x27 }, /* MYANMAR SIGN DOT BELOW -> ''' */
+ { 0x38, 0x3A }, /* MYANMAR SIGN VISARGA -> ':' */
+ { 0x40, 0x30 }, /* MYANMAR DIGIT ZERO -> '0' */
+ { 0x41, 0x31 }, /* MYANMAR DIGIT ONE -> '1' */
+ { 0x42, 0x32 }, /* MYANMAR DIGIT TWO -> '2' */
+ { 0x43, 0x33 }, /* MYANMAR DIGIT THREE -> '3' */
+ { 0x44, 0x34 }, /* MYANMAR DIGIT FOUR -> '4' */
+ { 0x45, 0x35 }, /* MYANMAR DIGIT FIVE -> '5' */
+ { 0x46, 0x36 }, /* MYANMAR DIGIT SIX -> '6' */
+ { 0x47, 0x37 }, /* MYANMAR DIGIT SEVEN -> '7' */
+ { 0x48, 0x38 }, /* MYANMAR DIGIT EIGHT -> '8' */
+ { 0x49, 0x39 }, /* MYANMAR DIGIT NINE -> '9' */
+ { 0x52, 0x52 }, /* MYANMAR LETTER VOCALIC R -> 'R' */
+ { 0x54, 0x4C }, /* MYANMAR LETTER VOCALIC L -> 'L' */
+ { 0x56, 0x52 }, /* MYANMAR VOWEL SIGN VOCALIC R -> 'R' */
+ { 0x58, 0x4C }, /* MYANMAR VOWEL SIGN VOCALIC L -> 'L' */
+ { 0xA0, 0x41 }, /* GEORGIAN CAPITAL LETTER AN -> 'A' */
+ { 0xA1, 0x42 }, /* GEORGIAN CAPITAL LETTER BAN -> 'B' */
+ { 0xA2, 0x47 }, /* GEORGIAN CAPITAL LETTER GAN -> 'G' */
+ { 0xA3, 0x44 }, /* GEORGIAN CAPITAL LETTER DON -> 'D' */
+ { 0xA4, 0x45 }, /* GEORGIAN CAPITAL LETTER EN -> 'E' */
+ { 0xA5, 0x56 }, /* GEORGIAN CAPITAL LETTER VIN -> 'V' */
+ { 0xA6, 0x5A }, /* GEORGIAN CAPITAL LETTER ZEN -> 'Z' */
+ { 0xA8, 0x49 }, /* GEORGIAN CAPITAL LETTER IN -> 'I' */
+ { 0xA9, 0x4B }, /* GEORGIAN CAPITAL LETTER KAN -> 'K' */
+ { 0xAA, 0x4C }, /* GEORGIAN CAPITAL LETTER LAS -> 'L' */
+ { 0xAB, 0x4D }, /* GEORGIAN CAPITAL LETTER MAN -> 'M' */
+ { 0xAC, 0x4E }, /* GEORGIAN CAPITAL LETTER NAR -> 'N' */
+ { 0xAD, 0x4F }, /* GEORGIAN CAPITAL LETTER ON -> 'O' */
+ { 0xAE, 0x50 }, /* GEORGIAN CAPITAL LETTER PAR -> 'P' */
+ { 0xB0, 0x52 }, /* GEORGIAN CAPITAL LETTER RAE -> 'R' */
+ { 0xB1, 0x53 }, /* GEORGIAN CAPITAL LETTER SAN -> 'S' */
+ { 0xB2, 0x54 }, /* GEORGIAN CAPITAL LETTER TAR -> 'T' */
+ { 0xB3, 0x55 }, /* GEORGIAN CAPITAL LETTER UN -> 'U' */
+ { 0xB7, 0x51 }, /* GEORGIAN CAPITAL LETTER QAR -> 'Q' */
+ { 0xBC, 0x43 }, /* GEORGIAN CAPITAL LETTER CIL -> 'C' */
+ { 0xBE, 0x58 }, /* GEORGIAN CAPITAL LETTER XAN -> 'X' */
+ { 0xBF, 0x4A }, /* GEORGIAN CAPITAL LETTER JHAN -> 'J' */
+ { 0xC0, 0x48 }, /* GEORGIAN CAPITAL LETTER HAE -> 'H' */
+ { 0xC1, 0x45 }, /* GEORGIAN CAPITAL LETTER HE -> 'E' */
+ { 0xC2, 0x59 }, /* GEORGIAN CAPITAL LETTER HIE -> 'Y' */
+ { 0xC3, 0x57 }, /* GEORGIAN CAPITAL LETTER WE -> 'W' */
+ { 0xD0, 0x61 }, /* GEORGIAN LETTER AN -> 'a' */
+ { 0xD1, 0x62 }, /* GEORGIAN LETTER BAN -> 'b' */
+ { 0xD2, 0x67 }, /* GEORGIAN LETTER GAN -> 'g' */
+ { 0xD3, 0x64 }, /* GEORGIAN LETTER DON -> 'd' */
+ { 0xD4, 0x65 }, /* GEORGIAN LETTER EN -> 'e' */
+ { 0xD5, 0x76 }, /* GEORGIAN LETTER VIN -> 'v' */
+ { 0xD6, 0x7A }, /* GEORGIAN LETTER ZEN -> 'z' */
+ { 0xD8, 0x69 }, /* GEORGIAN LETTER IN -> 'i' */
+ { 0xD9, 0x6B }, /* GEORGIAN LETTER KAN -> 'k' */
+ { 0xDA, 0x6C }, /* GEORGIAN LETTER LAS -> 'l' */
+ { 0xDB, 0x6D }, /* GEORGIAN LETTER MAN -> 'm' */
+ { 0xDC, 0x6E }, /* GEORGIAN LETTER NAR -> 'n' */
+ { 0xDD, 0x6F }, /* GEORGIAN LETTER ON -> 'o' */
+ { 0xDE, 0x70 }, /* GEORGIAN LETTER PAR -> 'p' */
+ { 0xE0, 0x72 }, /* GEORGIAN LETTER RAE -> 'r' */
+ { 0xE1, 0x73 }, /* GEORGIAN LETTER SAN -> 's' */
+ { 0xE2, 0x74 }, /* GEORGIAN LETTER TAR -> 't' */
+ { 0xE3, 0x75 }, /* GEORGIAN LETTER UN -> 'u' */
+ { 0xE7, 0x71 }, /* GEORGIAN LETTER QAR -> 'q' */
+ { 0xEC, 0x63 }, /* GEORGIAN LETTER CIL -> 'c' */
+ { 0xEE, 0x78 }, /* GEORGIAN LETTER XAN -> 'x' */
+ { 0xEF, 0x6A }, /* GEORGIAN LETTER JHAN -> 'j' */
+ { 0xF0, 0x68 }, /* GEORGIAN LETTER HAE -> 'h' */
+ { 0xF1, 0x65 }, /* GEORGIAN LETTER HE -> 'e' */
+ { 0xF2, 0x79 }, /* GEORGIAN LETTER HIE -> 'y' */
+ { 0xF3, 0x77 }, /* GEORGIAN LETTER WE -> 'w' */
+ { 0xF6, 0x66 }, /* GEORGIAN LETTER FI -> 'f' */
+ /* Entries for page 0x11 */
+ { 0x00, 0x67 }, /* HANGUL CHOSEONG KIYEOK -> 'g' */
+ { 0x02, 0x6E }, /* HANGUL CHOSEONG NIEUN -> 'n' */
+ { 0x03, 0x64 }, /* HANGUL CHOSEONG TIKEUT -> 'd' */
+ { 0x05, 0x72 }, /* HANGUL CHOSEONG RIEUL -> 'r' */
+ { 0x06, 0x6D }, /* HANGUL CHOSEONG MIEUM -> 'm' */
+ { 0x07, 0x62 }, /* HANGUL CHOSEONG PIEUP -> 'b' */
+ { 0x09, 0x73 }, /* HANGUL CHOSEONG SIOS -> 's' */
+ { 0x0C, 0x6A }, /* HANGUL CHOSEONG CIEUC -> 'j' */
+ { 0x0E, 0x63 }, /* HANGUL CHOSEONG CHIEUCH -> 'c' */
+ { 0x0F, 0x6B }, /* HANGUL CHOSEONG KHIEUKH -> 'k' */
+ { 0x10, 0x74 }, /* HANGUL CHOSEONG THIEUTH -> 't' */
+ { 0x11, 0x70 }, /* HANGUL CHOSEONG PHIEUPH -> 'p' */
+ { 0x12, 0x68 }, /* HANGUL CHOSEONG HIEUH -> 'h' */
+ { 0x35, 0x73 }, /* HANGUL CHOSEONG SIOS-IEUNG -> 's' */
+ { 0x40, 0x5A }, /* HANGUL CHOSEONG PANSIOS -> 'Z' */
+ { 0x41, 0x67 }, /* HANGUL CHOSEONG IEUNG-KIYEOK -> 'g' */
+ { 0x42, 0x64 }, /* HANGUL CHOSEONG IEUNG-TIKEUT -> 'd' */
+ { 0x43, 0x6D }, /* HANGUL CHOSEONG IEUNG-MIEUM -> 'm' */
+ { 0x44, 0x62 }, /* HANGUL CHOSEONG IEUNG-PIEUP -> 'b' */
+ { 0x45, 0x73 }, /* HANGUL CHOSEONG IEUNG-SIOS -> 's' */
+ { 0x46, 0x5A }, /* HANGUL CHOSEONG IEUNG-PANSIOS -> 'Z' */
+ { 0x48, 0x6A }, /* HANGUL CHOSEONG IEUNG-CIEUC -> 'j' */
+ { 0x49, 0x63 }, /* HANGUL CHOSEONG IEUNG-CHIEUCH -> 'c' */
+ { 0x4A, 0x74 }, /* HANGUL CHOSEONG IEUNG-THIEUTH -> 't' */
+ { 0x4B, 0x70 }, /* HANGUL CHOSEONG IEUNG-PHIEUPH -> 'p' */
+ { 0x4C, 0x4E }, /* HANGUL CHOSEONG YESIEUNG -> 'N' */
+ { 0x4D, 0x6A }, /* HANGUL CHOSEONG CIEUC-IEUNG -> 'j' */
+ { 0x59, 0x51 }, /* HANGUL CHOSEONG YEORINHIEUH -> 'Q' */
+ { 0x61, 0x61 }, /* HANGUL JUNGSEONG A -> 'a' */
+ { 0x66, 0x65 }, /* HANGUL JUNGSEONG E -> 'e' */
+ { 0x69, 0x6F }, /* HANGUL JUNGSEONG O -> 'o' */
+ { 0x6E, 0x75 }, /* HANGUL JUNGSEONG U -> 'u' */
+ { 0x75, 0x69 }, /* HANGUL JUNGSEONG I -> 'i' */
+ { 0x9E, 0x55 }, /* HANGUL JUNGSEONG ARAEA -> 'U' */
+ { 0xA8, 0x67 }, /* HANGUL JONGSEONG KIYEOK -> 'g' */
+ { 0xAB, 0x6E }, /* HANGUL JONGSEONG NIEUN -> 'n' */
+ { 0xAE, 0x64 }, /* HANGUL JONGSEONG TIKEUT -> 'd' */
+ { 0xAF, 0x6C }, /* HANGUL JONGSEONG RIEUL -> 'l' */
+ { 0xB7, 0x6D }, /* HANGUL JONGSEONG MIEUM -> 'm' */
+ { 0xB8, 0x62 }, /* HANGUL JONGSEONG PIEUP -> 'b' */
+ { 0xBA, 0x73 }, /* HANGUL JONGSEONG SIOS -> 's' */
+ { 0xBD, 0x6A }, /* HANGUL JONGSEONG CIEUC -> 'j' */
+ { 0xBE, 0x63 }, /* HANGUL JONGSEONG CHIEUCH -> 'c' */
+ { 0xBF, 0x6B }, /* HANGUL JONGSEONG KHIEUKH -> 'k' */
+ { 0xC0, 0x74 }, /* HANGUL JONGSEONG THIEUTH -> 't' */
+ { 0xC1, 0x70 }, /* HANGUL JONGSEONG PHIEUPH -> 'p' */
+ { 0xC2, 0x68 }, /* HANGUL JONGSEONG HIEUH -> 'h' */
+ { 0xEB, 0x5A }, /* HANGUL JONGSEONG PANSIOS -> 'Z' */
+ { 0xEC, 0x67 }, /* HANGUL JONGSEONG IEUNG-KIYEOK -> 'g' */
+ { 0xF0, 0x4E }, /* HANGUL JONGSEONG YESIEUNG -> 'N' */
+ { 0xF9, 0x51 }, /* HANGUL JONGSEONG YEORINHIEUH -> 'Q' */
+ /* Entries for page 0x13 */
+ { 0x61, 0x20 }, /* ETHIOPIC WORDSPACE -> ' ' */
+ { 0x62, 0x2E }, /* ETHIOPIC FULL STOP -> '.' */
+ { 0x63, 0x2C }, /* ETHIOPIC COMMA -> ',' */
+ { 0x64, 0x3B }, /* ETHIOPIC SEMICOLON -> ';' */
+ { 0x65, 0x3A }, /* ETHIOPIC COLON -> ':' */
+ { 0x67, 0x3F }, /* ETHIOPIC QUESTION MARK -> '?' */
+ { 0x69, 0x31 }, /* ETHIOPIC DIGIT ONE -> '1' */
+ { 0x6A, 0x32 }, /* ETHIOPIC DIGIT TWO -> '2' */
+ { 0x6B, 0x33 }, /* ETHIOPIC DIGIT THREE -> '3' */
+ { 0x6C, 0x34 }, /* ETHIOPIC DIGIT FOUR -> '4' */
+ { 0x6D, 0x35 }, /* ETHIOPIC DIGIT FIVE -> '5' */
+ { 0x6E, 0x36 }, /* ETHIOPIC DIGIT SIX -> '6' */
+ { 0x6F, 0x37 }, /* ETHIOPIC DIGIT SEVEN -> '7' */
+ { 0x70, 0x38 }, /* ETHIOPIC DIGIT EIGHT -> '8' */
+ { 0x71, 0x39 }, /* ETHIOPIC DIGIT NINE -> '9' */
+ { 0xA0, 0x61 }, /* CHEROKEE LETTER A -> 'a' */
+ { 0xA1, 0x65 }, /* CHEROKEE LETTER E -> 'e' */
+ { 0xA2, 0x69 }, /* CHEROKEE LETTER I -> 'i' */
+ { 0xA3, 0x6F }, /* CHEROKEE LETTER O -> 'o' */
+ { 0xA4, 0x75 }, /* CHEROKEE LETTER U -> 'u' */
+ { 0xA5, 0x76 }, /* CHEROKEE LETTER V -> 'v' */
+ { 0xCD, 0x73 }, /* CHEROKEE LETTER S -> 's' */
+ /* Entries for page 0x14 */
+ { 0x01, 0x65 }, /* CANADIAN SYLLABICS E -> 'e' */
+ { 0x03, 0x69 }, /* CANADIAN SYLLABICS I -> 'i' */
+ { 0x05, 0x6F }, /* CANADIAN SYLLABICS O -> 'o' */
+ { 0x09, 0x69 }, /* CANADIAN SYLLABICS CARRIER I -> 'i' */
+ { 0x0A, 0x61 }, /* CANADIAN SYLLABICS A -> 'a' */
+ { 0x1D, 0x77 }, /* CANADIAN SYLLABICS Y-CREE W -> 'w' */
+ { 0x1E, 0x27 }, /* CANADIAN SYLLABICS GLOTTAL STOP -> ''' */
+ { 0x1F, 0x74 }, /* CANADIAN SYLLABICS FINAL ACUTE -> 't' */
+ { 0x20, 0x6B }, /* CANADIAN SYLLABICS FINAL GRAVE -> 'k' */
+ { 0x22, 0x73 }, /* CANADIAN SYLLABICS FINAL TOP HALF RING -> 's' */
+ { 0x23, 0x6E }, /* CANADIAN SYLLABICS FINAL RIGHT HALF RING -> 'n' */
+ { 0x24, 0x77 }, /* CANADIAN SYLLABICS FINAL RING -> 'w' */
+ { 0x25, 0x6E }, /* CANADIAN SYLLABICS FINAL DOUBLE ACUTE -> 'n' */
+ { 0x27, 0x77 }, /* CANADIAN SYLLABICS FINAL MIDDLE DOT -> 'w' */
+ { 0x28, 0x63 }, /* CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE -> 'c' */
+ { 0x29, 0x3F }, /* CANADIAN SYLLABICS FINAL PLUS -> '?' */
+ { 0x2A, 0x6C }, /* CANADIAN SYLLABICS FINAL DOWN TACK -> 'l' */
+ { 0x49, 0x70 }, /* CANADIAN SYLLABICS P -> 'p' */
+ { 0x4A, 0x70 }, /* CANADIAN SYLLABICS WEST-CREE P -> 'p' */
+ { 0x4B, 0x68 }, /* CANADIAN SYLLABICS CARRIER H -> 'h' */
+ { 0x66, 0x74 }, /* CANADIAN SYLLABICS T -> 't' */
+ { 0x83, 0x6B }, /* CANADIAN SYLLABICS K -> 'k' */
+ { 0xA1, 0x63 }, /* CANADIAN SYLLABICS C -> 'c' */
+ { 0xBB, 0x6D }, /* CANADIAN SYLLABICS M -> 'm' */
+ { 0xBC, 0x6D }, /* CANADIAN SYLLABICS WEST-CREE M -> 'm' */
+ { 0xBE, 0x6D }, /* CANADIAN SYLLABICS ATHAPASCAN M -> 'm' */
+ { 0xBF, 0x6D }, /* CANADIAN SYLLABICS SAYISI M -> 'm' */
+ { 0xD0, 0x6E }, /* CANADIAN SYLLABICS N -> 'n' */
+ { 0xEA, 0x00 }, /* CANADIAN SYLLABICS L -> ... */
+ { 0xEC, 0x6C }, /* CANADIAN SYLLABICS MEDIAL L -> 'l' */
+ /* Entries for page 0x15 */
+ { 0x05, 0x73 }, /* CANADIAN SYLLABICS S -> 's' */
+ { 0x06, 0x73 }, /* CANADIAN SYLLABICS ATHAPASCAN S -> 's' */
+ { 0x08, 0x73 }, /* CANADIAN SYLLABICS BLACKFOOT S -> 's' */
+ { 0x3E, 0x00 }, /* CANADIAN SYLLABICS Y -> ... */
+ { 0x40, 0x79 }, /* CANADIAN SYLLABICS WEST-CREE Y -> 'y' */
+ { 0x50, 0x00 }, /* CANADIAN SYLLABICS R -> ... */
+ { 0x52, 0x72 }, /* CANADIAN SYLLABICS MEDIAL R -> 'r' */
+ { 0x5D, 0x66 }, /* CANADIAN SYLLABICS F -> 'f' */
+ { 0x7B, 0x68 }, /* CANADIAN SYLLABICS NUNAVIK H -> 'h' */
+ { 0x7C, 0x68 }, /* CANADIAN SYLLABICS NUNAVUT H -> 'h' */
+ { 0x85, 0x71 }, /* CANADIAN SYLLABICS Q -> 'q' */
+ { 0xAF, 0x62 }, /* CANADIAN SYLLABICS AIVILIK B -> 'b' */
+ { 0xB0, 0x65 }, /* CANADIAN SYLLABICS BLACKFOOT E -> 'e' */
+ { 0xB1, 0x69 }, /* CANADIAN SYLLABICS BLACKFOOT I -> 'i' */
+ { 0xB2, 0x6F }, /* CANADIAN SYLLABICS BLACKFOOT O -> 'o' */
+ { 0xB3, 0x61 }, /* CANADIAN SYLLABICS BLACKFOOT A -> 'a' */
+ { 0xEE, 0x70 }, /* CANADIAN SYLLABICS CARRIER P -> 'p' */
+ /* Entries for page 0x16 */
+ { 0x46, 0x7A }, /* CANADIAN SYLLABICS CARRIER Z -> 'z' */
+ { 0x47, 0x7A }, /* CANADIAN SYLLABICS CARRIER INITIAL Z -> 'z' */
+ { 0x6D, 0x58 }, /* CANADIAN SYLLABICS CHI SIGN -> 'X' */
+ { 0x6E, 0x2E }, /* CANADIAN SYLLABICS FULL STOP -> '.' */
+ { 0x80, 0x20 }, /* OGHAM SPACE MARK -> ' ' */
+ { 0x81, 0x62 }, /* OGHAM LETTER BEITH -> 'b' */
+ { 0x82, 0x6C }, /* OGHAM LETTER LUIS -> 'l' */
+ { 0x83, 0x66 }, /* OGHAM LETTER FEARN -> 'f' */
+ { 0x84, 0x73 }, /* OGHAM LETTER SAIL -> 's' */
+ { 0x85, 0x6E }, /* OGHAM LETTER NION -> 'n' */
+ { 0x86, 0x68 }, /* OGHAM LETTER UATH -> 'h' */
+ { 0x87, 0x64 }, /* OGHAM LETTER DAIR -> 'd' */
+ { 0x88, 0x74 }, /* OGHAM LETTER TINNE -> 't' */
+ { 0x89, 0x63 }, /* OGHAM LETTER COLL -> 'c' */
+ { 0x8A, 0x71 }, /* OGHAM LETTER CEIRT -> 'q' */
+ { 0x8B, 0x6D }, /* OGHAM LETTER MUIN -> 'm' */
+ { 0x8C, 0x67 }, /* OGHAM LETTER GORT -> 'g' */
+ { 0x8E, 0x7A }, /* OGHAM LETTER STRAIF -> 'z' */
+ { 0x8F, 0x72 }, /* OGHAM LETTER RUIS -> 'r' */
+ { 0x90, 0x61 }, /* OGHAM LETTER AILM -> 'a' */
+ { 0x91, 0x6F }, /* OGHAM LETTER ONN -> 'o' */
+ { 0x92, 0x75 }, /* OGHAM LETTER UR -> 'u' */
+ { 0x93, 0x65 }, /* OGHAM LETTER EADHADH -> 'e' */
+ { 0x94, 0x69 }, /* OGHAM LETTER IODHADH -> 'i' */
+ { 0x98, 0x70 }, /* OGHAM LETTER IFIN -> 'p' */
+ { 0x99, 0x78 }, /* OGHAM LETTER EAMHANCHOLL -> 'x' */
+ { 0x9A, 0x70 }, /* OGHAM LETTER PEITH -> 'p' */
+ { 0x9B, 0x3C }, /* OGHAM FEATHER MARK -> '<' */
+ { 0x9C, 0x3E }, /* OGHAM REVERSED FEATHER MARK -> '>' */
+ { 0xA0, 0x66 }, /* RUNIC LETTER FEHU FEOH FE F -> 'f' */
+ { 0xA1, 0x76 }, /* RUNIC LETTER V -> 'v' */
+ { 0xA2, 0x75 }, /* RUNIC LETTER URUZ UR U -> 'u' */
+ { 0xA4, 0x79 }, /* RUNIC LETTER Y -> 'y' */
+ { 0xA5, 0x77 }, /* RUNIC LETTER W -> 'w' */
+ { 0xA8, 0x61 }, /* RUNIC LETTER ANSUZ A -> 'a' */
+ { 0xA9, 0x6F }, /* RUNIC LETTER OS O -> 'o' */
+ { 0xAC, 0x00 }, /* RUNIC LETTER LONG-BRANCH-OSS O -> ... */
+ { 0xAE, 0x6F }, /* RUNIC LETTER O -> 'o' */
+ { 0xB1, 0x72 }, /* RUNIC LETTER RAIDO RAD REID R -> 'r' */
+ { 0xB2, 0x6B }, /* RUNIC LETTER KAUNA -> 'k' */
+ { 0xB3, 0x63 }, /* RUNIC LETTER CEN -> 'c' */
+ { 0xB4, 0x6B }, /* RUNIC LETTER KAUN K -> 'k' */
+ { 0xB5, 0x67 }, /* RUNIC LETTER G -> 'g' */
+ { 0xB7, 0x67 }, /* RUNIC LETTER GEBO GYFU G -> 'g' */
+ { 0xB8, 0x67 }, /* RUNIC LETTER GAR -> 'g' */
+ { 0xB9, 0x77 }, /* RUNIC LETTER WUNJO WYNN W -> 'w' */
+ { 0xBA, 0x00 }, /* RUNIC LETTER HAGLAZ H -> ... */
+ { 0xBD, 0x68 }, /* RUNIC LETTER SHORT-TWIG-HAGALL H -> 'h' */
+ { 0xBE, 0x00 }, /* RUNIC LETTER NAUDIZ NYD NAUD N -> ... */
+ { 0xC0, 0x6E }, /* RUNIC LETTER DOTTED-N -> 'n' */
+ { 0xC1, 0x69 }, /* RUNIC LETTER ISAZ IS ISS I -> 'i' */
+ { 0xC2, 0x65 }, /* RUNIC LETTER E -> 'e' */
+ { 0xC3, 0x6A }, /* RUNIC LETTER JERAN J -> 'j' */
+ { 0xC4, 0x67 }, /* RUNIC LETTER GER -> 'g' */
+ { 0xC6, 0x61 }, /* RUNIC LETTER SHORT-TWIG-AR A -> 'a' */
+ { 0xC8, 0x70 }, /* RUNIC LETTER PERTHO PEORTH P -> 'p' */
+ { 0xC9, 0x7A }, /* RUNIC LETTER ALGIZ EOLHX -> 'z' */
+ { 0xCA, 0x00 }, /* RUNIC LETTER SOWILO S -> ... */
+ { 0xCC, 0x73 }, /* RUNIC LETTER SHORT-TWIG-SOL S -> 's' */
+ { 0xCD, 0x63 }, /* RUNIC LETTER C -> 'c' */
+ { 0xCE, 0x7A }, /* RUNIC LETTER Z -> 'z' */
+ { 0xCF, 0x74 }, /* RUNIC LETTER TIWAZ TIR TYR T -> 't' */
+ { 0xD0, 0x74 }, /* RUNIC LETTER SHORT-TWIG-TYR T -> 't' */
+ { 0xD1, 0x64 }, /* RUNIC LETTER D -> 'd' */
+ { 0xD2, 0x62 }, /* RUNIC LETTER BERKANAN BEORC BJARKAN B -> 'b' */
+ { 0xD3, 0x62 }, /* RUNIC LETTER SHORT-TWIG-BJARKAN B -> 'b' */
+ { 0xD4, 0x70 }, /* RUNIC LETTER DOTTED-P -> 'p' */
+ { 0xD5, 0x70 }, /* RUNIC LETTER OPEN-P -> 'p' */
+ { 0xD6, 0x65 }, /* RUNIC LETTER EHWAZ EH E -> 'e' */
+ { 0xD7, 0x00 }, /* RUNIC LETTER MANNAZ MAN M -> ... */
+ { 0xD9, 0x6D }, /* RUNIC LETTER SHORT-TWIG-MADR M -> 'm' */
+ { 0xDA, 0x6C }, /* RUNIC LETTER LAUKAZ LAGU LOGR L -> 'l' */
+ { 0xDB, 0x6C }, /* RUNIC LETTER DOTTED-L -> 'l' */
+ { 0xDE, 0x64 }, /* RUNIC LETTER DAGAZ DAEG D -> 'd' */
+ { 0xDF, 0x6F }, /* RUNIC LETTER OTHALAN ETHEL O -> 'o' */
+ { 0xE5, 0x73 }, /* RUNIC LETTER STAN -> 's' */
+ { 0xE9, 0x71 }, /* RUNIC LETTER Q -> 'q' */
+ { 0xEA, 0x78 }, /* RUNIC LETTER X -> 'x' */
+ { 0xEB, 0x2E }, /* RUNIC SINGLE PUNCTUATION -> '.' */
+ { 0xEC, 0x3A }, /* RUNIC MULTIPLE PUNCTUATION -> ':' */
+ { 0xED, 0x2B }, /* RUNIC CROSS PUNCTUATION -> '+' */
+ /* Entries for page 0x17 */
+ { 0x80, 0x6B }, /* KHMER LETTER KA -> 'k' */
+ { 0x82, 0x67 }, /* KHMER LETTER KO -> 'g' */
+ { 0x85, 0x63 }, /* KHMER LETTER CA -> 'c' */
+ { 0x87, 0x6A }, /* KHMER LETTER CO -> 'j' */
+ { 0x8A, 0x74 }, /* KHMER LETTER DA -> 't' */
+ { 0x8C, 0x64 }, /* KHMER LETTER DO -> 'd' */
+ { 0x8F, 0x74 }, /* KHMER LETTER TA -> 't' */
+ { 0x91, 0x64 }, /* KHMER LETTER TO -> 'd' */
+ { 0x93, 0x6E }, /* KHMER LETTER NO -> 'n' */
+ { 0x94, 0x70 }, /* KHMER LETTER BA -> 'p' */
+ { 0x96, 0x62 }, /* KHMER LETTER PO -> 'b' */
+ { 0x98, 0x6D }, /* KHMER LETTER MO -> 'm' */
+ { 0x99, 0x79 }, /* KHMER LETTER YO -> 'y' */
+ { 0x9A, 0x72 }, /* KHMER LETTER RO -> 'r' */
+ { 0x9B, 0x6C }, /* KHMER LETTER LO -> 'l' */
+ { 0x9C, 0x76 }, /* KHMER LETTER VO -> 'v' */
+ { 0x9F, 0x73 }, /* KHMER LETTER SA -> 's' */
+ { 0xA0, 0x68 }, /* KHMER LETTER HA -> 'h' */
+ { 0xA1, 0x6C }, /* KHMER LETTER LA -> 'l' */
+ { 0xA2, 0x71 }, /* KHMER LETTER QA -> 'q' */
+ { 0xA3, 0x61 }, /* KHMER INDEPENDENT VOWEL QAQ -> 'a' */
+ { 0xA5, 0x69 }, /* KHMER INDEPENDENT VOWEL QI -> 'i' */
+ { 0xA7, 0x75 }, /* KHMER INDEPENDENT VOWEL QU -> 'u' */
+ { 0xAF, 0x65 }, /* KHMER INDEPENDENT VOWEL QE -> 'e' */
+ { 0xB4, 0x61 }, /* KHMER VOWEL INHERENT AQ -> 'a' */
+ { 0xB7, 0x69 }, /* KHMER VOWEL SIGN I -> 'i' */
+ { 0xB9, 0x79 }, /* KHMER VOWEL SIGN Y -> 'y' */
+ { 0xBB, 0x75 }, /* KHMER VOWEL SIGN U -> 'u' */
+ { 0xC1, 0x65 }, /* KHMER VOWEL SIGN E -> 'e' */
+ { 0xC6, 0x4D }, /* KHMER SIGN NIKAHIT -> 'M' */
+ { 0xC7, 0x48 }, /* KHMER SIGN REAHMUK -> 'H' */
+ { 0xCC, 0x72 }, /* KHMER SIGN ROBAT -> 'r' */
+ { 0xCE, 0x21 }, /* KHMER SIGN KAKABAT -> '!' */
+ { 0xD4, 0x2E }, /* KHMER SIGN KHAN -> '.' */
+ { 0xD6, 0x3A }, /* KHMER SIGN CAMNUC PII KUUH -> ':' */
+ { 0xD7, 0x2B }, /* KHMER SIGN LEK TOO -> '+' */
+ { 0xDC, 0x27 }, /* KHMER SIGN AVAKRAHASANYA -> ''' */
+ { 0xE0, 0x30 }, /* KHMER DIGIT ZERO -> '0' */
+ { 0xE1, 0x31 }, /* KHMER DIGIT ONE -> '1' */
+ { 0xE2, 0x32 }, /* KHMER DIGIT TWO -> '2' */
+ { 0xE3, 0x33 }, /* KHMER DIGIT THREE -> '3' */
+ { 0xE4, 0x34 }, /* KHMER DIGIT FOUR -> '4' */
+ { 0xE5, 0x35 }, /* KHMER DIGIT FIVE -> '5' */
+ { 0xE6, 0x36 }, /* KHMER DIGIT SIX -> '6' */
+ { 0xE7, 0x37 }, /* KHMER DIGIT SEVEN -> '7' */
+ { 0xE8, 0x38 }, /* KHMER DIGIT EIGHT -> '8' */
+ { 0xE9, 0x39 }, /* KHMER DIGIT NINE -> '9' */
+ /* Entries for page 0x18 */
+ { 0x07, 0x2D }, /* MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER -> '-' */
+ { 0x10, 0x30 }, /* MONGOLIAN DIGIT ZERO -> '0' */
+ { 0x11, 0x31 }, /* MONGOLIAN DIGIT ONE -> '1' */
+ { 0x12, 0x32 }, /* MONGOLIAN DIGIT TWO -> '2' */
+ { 0x13, 0x33 }, /* MONGOLIAN DIGIT THREE -> '3' */
+ { 0x14, 0x34 }, /* MONGOLIAN DIGIT FOUR -> '4' */
+ { 0x15, 0x35 }, /* MONGOLIAN DIGIT FIVE -> '5' */
+ { 0x16, 0x36 }, /* MONGOLIAN DIGIT SIX -> '6' */
+ { 0x17, 0x37 }, /* MONGOLIAN DIGIT SEVEN -> '7' */
+ { 0x18, 0x38 }, /* MONGOLIAN DIGIT EIGHT -> '8' */
+ { 0x19, 0x39 }, /* MONGOLIAN DIGIT NINE -> '9' */
+ { 0x20, 0x61 }, /* MONGOLIAN LETTER A -> 'a' */
+ { 0x21, 0x65 }, /* MONGOLIAN LETTER E -> 'e' */
+ { 0x22, 0x69 }, /* MONGOLIAN LETTER I -> 'i' */
+ { 0x23, 0x6F }, /* MONGOLIAN LETTER O -> 'o' */
+ { 0x24, 0x75 }, /* MONGOLIAN LETTER U -> 'u' */
+ { 0x25, 0x4F }, /* MONGOLIAN LETTER OE -> 'O' */
+ { 0x26, 0x55 }, /* MONGOLIAN LETTER UE -> 'U' */
+ { 0x28, 0x6E }, /* MONGOLIAN LETTER NA -> 'n' */
+ { 0x2A, 0x62 }, /* MONGOLIAN LETTER BA -> 'b' */
+ { 0x2B, 0x70 }, /* MONGOLIAN LETTER PA -> 'p' */
+ { 0x2C, 0x71 }, /* MONGOLIAN LETTER QA -> 'q' */
+ { 0x2D, 0x67 }, /* MONGOLIAN LETTER GA -> 'g' */
+ { 0x2E, 0x6D }, /* MONGOLIAN LETTER MA -> 'm' */
+ { 0x2F, 0x6C }, /* MONGOLIAN LETTER LA -> 'l' */
+ { 0x30, 0x73 }, /* MONGOLIAN LETTER SA -> 's' */
+ { 0x32, 0x74 }, /* MONGOLIAN LETTER TA -> 't' */
+ { 0x33, 0x64 }, /* MONGOLIAN LETTER DA -> 'd' */
+ { 0x35, 0x6A }, /* MONGOLIAN LETTER JA -> 'j' */
+ { 0x36, 0x79 }, /* MONGOLIAN LETTER YA -> 'y' */
+ { 0x37, 0x72 }, /* MONGOLIAN LETTER RA -> 'r' */
+ { 0x38, 0x77 }, /* MONGOLIAN LETTER WA -> 'w' */
+ { 0x39, 0x66 }, /* MONGOLIAN LETTER FA -> 'f' */
+ { 0x3A, 0x6B }, /* MONGOLIAN LETTER KA -> 'k' */
+ { 0x3D, 0x7A }, /* MONGOLIAN LETTER ZA -> 'z' */
+ { 0x3E, 0x68 }, /* MONGOLIAN LETTER HAA -> 'h' */
+ { 0x43, 0x2D }, /* MONGOLIAN LETTER TODO LONG VOWEL SIGN -> '-' */
+ { 0x44, 0x65 }, /* MONGOLIAN LETTER TODO E -> 'e' */
+ { 0x45, 0x69 }, /* MONGOLIAN LETTER TODO I -> 'i' */
+ { 0x46, 0x6F }, /* MONGOLIAN LETTER TODO O -> 'o' */
+ { 0x47, 0x75 }, /* MONGOLIAN LETTER TODO U -> 'u' */
+ { 0x48, 0x4F }, /* MONGOLIAN LETTER TODO OE -> 'O' */
+ { 0x49, 0x55 }, /* MONGOLIAN LETTER TODO UE -> 'U' */
+ { 0x4B, 0x62 }, /* MONGOLIAN LETTER TODO BA -> 'b' */
+ { 0x4C, 0x70 }, /* MONGOLIAN LETTER TODO PA -> 'p' */
+ { 0x4D, 0x71 }, /* MONGOLIAN LETTER TODO QA -> 'q' */
+ { 0x4E, 0x67 }, /* MONGOLIAN LETTER TODO GA -> 'g' */
+ { 0x4F, 0x6D }, /* MONGOLIAN LETTER TODO MA -> 'm' */
+ { 0x50, 0x74 }, /* MONGOLIAN LETTER TODO TA -> 't' */
+ { 0x51, 0x64 }, /* MONGOLIAN LETTER TODO DA -> 'd' */
+ { 0x53, 0x6A }, /* MONGOLIAN LETTER TODO JA -> 'j' */
+ { 0x55, 0x79 }, /* MONGOLIAN LETTER TODO YA -> 'y' */
+ { 0x56, 0x77 }, /* MONGOLIAN LETTER TODO WA -> 'w' */
+ { 0x57, 0x6B }, /* MONGOLIAN LETTER TODO KA -> 'k' */
+ { 0x58, 0x67 }, /* MONGOLIAN LETTER TODO GAA -> 'g' */
+ { 0x59, 0x68 }, /* MONGOLIAN LETTER TODO HAA -> 'h' */
+ { 0x5D, 0x65 }, /* MONGOLIAN LETTER SIBE E -> 'e' */
+ { 0x5E, 0x69 }, /* MONGOLIAN LETTER SIBE I -> 'i' */
+ { 0x60, 0x55 }, /* MONGOLIAN LETTER SIBE UE -> 'U' */
+ { 0x61, 0x75 }, /* MONGOLIAN LETTER SIBE U -> 'u' */
+ { 0x63, 0x6B }, /* MONGOLIAN LETTER SIBE KA -> 'k' */
+ { 0x64, 0x67 }, /* MONGOLIAN LETTER SIBE GA -> 'g' */
+ { 0x65, 0x68 }, /* MONGOLIAN LETTER SIBE HA -> 'h' */
+ { 0x66, 0x70 }, /* MONGOLIAN LETTER SIBE PA -> 'p' */
+ { 0x68, 0x74 }, /* MONGOLIAN LETTER SIBE TA -> 't' */
+ { 0x69, 0x64 }, /* MONGOLIAN LETTER SIBE DA -> 'd' */
+ { 0x6A, 0x6A }, /* MONGOLIAN LETTER SIBE JA -> 'j' */
+ { 0x6B, 0x66 }, /* MONGOLIAN LETTER SIBE FA -> 'f' */
+ { 0x6C, 0x67 }, /* MONGOLIAN LETTER SIBE GAA -> 'g' */
+ { 0x6D, 0x68 }, /* MONGOLIAN LETTER SIBE HAA -> 'h' */
+ { 0x6F, 0x7A }, /* MONGOLIAN LETTER SIBE ZA -> 'z' */
+ { 0x70, 0x72 }, /* MONGOLIAN LETTER SIBE RAA -> 'r' */
+ { 0x73, 0x69 }, /* MONGOLIAN LETTER MANCHU I -> 'i' */
+ { 0x74, 0x6B }, /* MONGOLIAN LETTER MANCHU KA -> 'k' */
+ { 0x75, 0x72 }, /* MONGOLIAN LETTER MANCHU RA -> 'r' */
+ { 0x76, 0x66 }, /* MONGOLIAN LETTER MANCHU FA -> 'f' */
+ { 0x81, 0x48 }, /* MONGOLIAN LETTER ALI GALI VISARGA ONE -> 'H' */
+ { 0x82, 0x58 }, /* MONGOLIAN LETTER ALI GALI DAMARU -> 'X' */
+ { 0x83, 0x57 }, /* MONGOLIAN LETTER ALI GALI UBADAMA -> 'W' */
+ { 0x84, 0x4D }, /* MONGOLIAN LETTER ALI GALI INVERTED UBADAMA -> 'M' */
+ { 0x87, 0x61 }, /* MONGOLIAN LETTER ALI GALI A -> 'a' */
+ { 0x88, 0x69 }, /* MONGOLIAN LETTER ALI GALI I -> 'i' */
+ { 0x89, 0x6B }, /* MONGOLIAN LETTER ALI GALI KA -> 'k' */
+ { 0x8B, 0x63 }, /* MONGOLIAN LETTER ALI GALI CA -> 'c' */
+ { 0x90, 0x74 }, /* MONGOLIAN LETTER ALI GALI TA -> 't' */
+ { 0x91, 0x64 }, /* MONGOLIAN LETTER ALI GALI DA -> 'd' */
+ { 0x92, 0x70 }, /* MONGOLIAN LETTER ALI GALI PA -> 'p' */
+ { 0x96, 0x7A }, /* MONGOLIAN LETTER ALI GALI ZA -> 'z' */
+ { 0x97, 0x61 }, /* MONGOLIAN LETTER ALI GALI AH -> 'a' */
+ { 0x98, 0x74 }, /* MONGOLIAN LETTER TODO ALI GALI TA -> 't' */
+ { 0x9C, 0x63 }, /* MONGOLIAN LETTER MANCHU ALI GALI CA -> 'c' */
+ { 0xA0, 0x74 }, /* MONGOLIAN LETTER MANCHU ALI GALI TA -> 't' */
+ { 0xA5, 0x7A }, /* MONGOLIAN LETTER MANCHU ALI GALI ZA -> 'z' */
+ { 0xA6, 0x75 }, /* MONGOLIAN LETTER ALI GALI HALF U -> 'u' */
+ { 0xA7, 0x79 }, /* MONGOLIAN LETTER ALI GALI HALF YA -> 'y' */
+ { 0xA9, 0x27 }, /* MONGOLIAN LETTER ALI GALI DAGALGA -> ''' */
+ /* Entries for page 0x1D */
+ { 0x00, 0x41 }, /* LATIN LETTER SMALL CAPITAL A -> 'A' */
+ { 0x03, 0x42 }, /* LATIN LETTER SMALL CAPITAL BARRED B -> 'B' */
+ { 0x04, 0x43 }, /* LATIN LETTER SMALL CAPITAL C -> 'C' */
+ { 0x05, 0x44 }, /* LATIN LETTER SMALL CAPITAL D -> 'D' */
+ { 0x06, 0x44 }, /* LATIN LETTER SMALL CAPITAL ETH -> 'D' */
+ { 0x07, 0x45 }, /* LATIN LETTER SMALL CAPITAL E -> 'E' */
+ { 0x08, 0x65 }, /* LATIN SMALL LETTER TURNED OPEN E -> 'e' */
+ { 0x09, 0x69 }, /* LATIN SMALL LETTER TURNED I -> 'i' */
+ { 0x0A, 0x4A }, /* LATIN LETTER SMALL CAPITAL J -> 'J' */
+ { 0x0B, 0x4B }, /* LATIN LETTER SMALL CAPITAL K -> 'K' */
+ { 0x0C, 0x4C }, /* LATIN LETTER SMALL CAPITAL L WITH STROKE -> 'L' */
+ { 0x0D, 0x4D }, /* LATIN LETTER SMALL CAPITAL M -> 'M' */
+ { 0x0E, 0x4E }, /* LATIN LETTER SMALL CAPITAL REVERSED N -> 'N' */
+ { 0x0F, 0x4F }, /* LATIN LETTER SMALL CAPITAL O -> 'O' */
+ { 0x11, 0x4F }, /* LATIN SMALL LETTER SIDEWAYS O -> 'O' */
+ { 0x13, 0x4F }, /* LATIN SMALL LETTER SIDEWAYS O WITH STROKE -> 'O' */
+ { 0x18, 0x50 }, /* LATIN LETTER SMALL CAPITAL P -> 'P' */
+ { 0x19, 0x52 }, /* LATIN LETTER SMALL CAPITAL REVERSED R -> 'R' */
+ { 0x1A, 0x52 }, /* LATIN LETTER SMALL CAPITAL TURNED R -> 'R' */
+ { 0x1B, 0x54 }, /* LATIN LETTER SMALL CAPITAL T -> 'T' */
+ { 0x1C, 0x55 }, /* LATIN LETTER SMALL CAPITAL U -> 'U' */
+ { 0x1D, 0x75 }, /* LATIN SMALL LETTER SIDEWAYS U -> 'u' */
+ { 0x1E, 0x75 }, /* LATIN SMALL LETTER SIDEWAYS DIAERESIZED U -> 'u' */
+ { 0x1F, 0x6D }, /* LATIN SMALL LETTER SIDEWAYS TURNED M -> 'm' */
+ { 0x20, 0x56 }, /* LATIN LETTER SMALL CAPITAL V -> 'V' */
+ { 0x21, 0x57 }, /* LATIN LETTER SMALL CAPITAL W -> 'W' */
+ { 0x22, 0x5A }, /* LATIN LETTER SMALL CAPITAL Z -> 'Z' */
+ { 0x2C, 0x41 }, /* MODIFIER LETTER CAPITAL A -> 'A' */
+ { 0x2E, 0x42 }, /* MODIFIER LETTER CAPITAL B -> 'B' */
+ { 0x2F, 0x42 }, /* MODIFIER LETTER CAPITAL BARRED B -> 'B' */
+ { 0x30, 0x44 }, /* MODIFIER LETTER CAPITAL D -> 'D' */
+ { 0x31, 0x45 }, /* MODIFIER LETTER CAPITAL E -> 'E' */
+ { 0x32, 0x45 }, /* MODIFIER LETTER CAPITAL REVERSED E -> 'E' */
+ { 0x33, 0x47 }, /* MODIFIER LETTER CAPITAL G -> 'G' */
+ { 0x34, 0x48 }, /* MODIFIER LETTER CAPITAL H -> 'H' */
+ { 0x35, 0x49 }, /* MODIFIER LETTER CAPITAL I -> 'I' */
+ { 0x36, 0x4A }, /* MODIFIER LETTER CAPITAL J -> 'J' */
+ { 0x37, 0x4B }, /* MODIFIER LETTER CAPITAL K -> 'K' */
+ { 0x38, 0x4C }, /* MODIFIER LETTER CAPITAL L -> 'L' */
+ { 0x39, 0x4D }, /* MODIFIER LETTER CAPITAL M -> 'M' */
+ { 0x3A, 0x4E }, /* MODIFIER LETTER CAPITAL N -> 'N' */
+ { 0x3B, 0x4E }, /* MODIFIER LETTER CAPITAL REVERSED N -> 'N' */
+ { 0x3C, 0x4F }, /* MODIFIER LETTER CAPITAL O -> 'O' */
+ { 0x3E, 0x50 }, /* MODIFIER LETTER CAPITAL P -> 'P' */
+ { 0x3F, 0x52 }, /* MODIFIER LETTER CAPITAL R -> 'R' */
+ { 0x40, 0x54 }, /* MODIFIER LETTER CAPITAL T -> 'T' */
+ { 0x41, 0x55 }, /* MODIFIER LETTER CAPITAL U -> 'U' */
+ { 0x42, 0x57 }, /* MODIFIER LETTER CAPITAL W -> 'W' */
+ { 0x43, 0x00 }, /* MODIFIER LETTER SMALL A -> ... */
+ { 0x45, 0x61 }, /* MODIFIER LETTER SMALL ALPHA -> 'a' */
+ { 0x47, 0x62 }, /* MODIFIER LETTER SMALL B -> 'b' */
+ { 0x48, 0x64 }, /* MODIFIER LETTER SMALL D -> 'd' */
+ { 0x49, 0x65 }, /* MODIFIER LETTER SMALL E -> 'e' */
+ { 0x4B, 0x65 }, /* MODIFIER LETTER SMALL OPEN E -> 'e' */
+ { 0x4C, 0x65 }, /* MODIFIER LETTER SMALL TURNED OPEN E -> 'e' */
+ { 0x4D, 0x67 }, /* MODIFIER LETTER SMALL G -> 'g' */
+ { 0x4E, 0x69 }, /* MODIFIER LETTER SMALL TURNED I -> 'i' */
+ { 0x4F, 0x6B }, /* MODIFIER LETTER SMALL K -> 'k' */
+ { 0x50, 0x6D }, /* MODIFIER LETTER SMALL M -> 'm' */
+ { 0x52, 0x6F }, /* MODIFIER LETTER SMALL O -> 'o' */
+ { 0x56, 0x70 }, /* MODIFIER LETTER SMALL P -> 'p' */
+ { 0x57, 0x74 }, /* MODIFIER LETTER SMALL T -> 't' */
+ { 0x58, 0x75 }, /* MODIFIER LETTER SMALL U -> 'u' */
+ { 0x59, 0x75 }, /* MODIFIER LETTER SMALL SIDEWAYS U -> 'u' */
+ { 0x5A, 0x6D }, /* MODIFIER LETTER SMALL TURNED M -> 'm' */
+ { 0x5B, 0x76 }, /* MODIFIER LETTER SMALL V -> 'v' */
+ { 0x5D, 0x62 }, /* MODIFIER LETTER SMALL BETA -> 'b' */
+ { 0x5E, 0x67 }, /* MODIFIER LETTER SMALL GREEK GAMMA -> 'g' */
+ { 0x5F, 0x64 }, /* MODIFIER LETTER SMALL DELTA -> 'd' */
+ { 0x60, 0x66 }, /* MODIFIER LETTER SMALL GREEK PHI -> 'f' */
+ { 0x62, 0x69 }, /* LATIN SUBSCRIPT SMALL LETTER I -> 'i' */
+ { 0x63, 0x72 }, /* LATIN SUBSCRIPT SMALL LETTER R -> 'r' */
+ { 0x64, 0x75 }, /* LATIN SUBSCRIPT SMALL LETTER U -> 'u' */
+ { 0x65, 0x76 }, /* LATIN SUBSCRIPT SMALL LETTER V -> 'v' */
+ { 0x66, 0x62 }, /* GREEK SUBSCRIPT SMALL LETTER BETA -> 'b' */
+ { 0x67, 0x67 }, /* GREEK SUBSCRIPT SMALL LETTER GAMMA -> 'g' */
+ { 0x68, 0x72 }, /* GREEK SUBSCRIPT SMALL LETTER RHO -> 'r' */
+ { 0x69, 0x66 }, /* GREEK SUBSCRIPT SMALL LETTER PHI -> 'f' */
+ { 0x6C, 0x62 }, /* LATIN SMALL LETTER B WITH MIDDLE TILDE -> 'b' */
+ { 0x6D, 0x64 }, /* LATIN SMALL LETTER D WITH MIDDLE TILDE -> 'd' */
+ { 0x6E, 0x66 }, /* LATIN SMALL LETTER F WITH MIDDLE TILDE -> 'f' */
+ { 0x6F, 0x6D }, /* LATIN SMALL LETTER M WITH MIDDLE TILDE -> 'm' */
+ { 0x70, 0x6E }, /* LATIN SMALL LETTER N WITH MIDDLE TILDE -> 'n' */
+ { 0x71, 0x70 }, /* LATIN SMALL LETTER P WITH MIDDLE TILDE -> 'p' */
+ { 0x72, 0x72 }, /* LATIN SMALL LETTER R WITH MIDDLE TILDE -> 'r' */
+ { 0x73, 0x72 }, /* LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE -> 'r' */
+ { 0x74, 0x73 }, /* LATIN SMALL LETTER S WITH MIDDLE TILDE -> 's' */
+ { 0x75, 0x74 }, /* LATIN SMALL LETTER T WITH MIDDLE TILDE -> 't' */
+ { 0x76, 0x7A }, /* LATIN SMALL LETTER Z WITH MIDDLE TILDE -> 'z' */
+ { 0x77, 0x67 }, /* LATIN SMALL LETTER TURNED G -> 'g' */
+ { 0x7D, 0x70 }, /* LATIN SMALL LETTER P WITH STROKE -> 'p' */
+ { 0x80, 0x62 }, /* LATIN SMALL LETTER B WITH PALATAL HOOK -> 'b' */
+ { 0x81, 0x64 }, /* LATIN SMALL LETTER D WITH PALATAL HOOK -> 'd' */
+ { 0x82, 0x66 }, /* LATIN SMALL LETTER F WITH PALATAL HOOK -> 'f' */
+ { 0x83, 0x67 }, /* LATIN SMALL LETTER G WITH PALATAL HOOK -> 'g' */
+ { 0x84, 0x6B }, /* LATIN SMALL LETTER K WITH PALATAL HOOK -> 'k' */
+ { 0x85, 0x6C }, /* LATIN SMALL LETTER L WITH PALATAL HOOK -> 'l' */
+ { 0x86, 0x6D }, /* LATIN SMALL LETTER M WITH PALATAL HOOK -> 'm' */
+ { 0x87, 0x6E }, /* LATIN SMALL LETTER N WITH PALATAL HOOK -> 'n' */
+ { 0x88, 0x70 }, /* LATIN SMALL LETTER P WITH PALATAL HOOK -> 'p' */
+ { 0x89, 0x72 }, /* LATIN SMALL LETTER R WITH PALATAL HOOK -> 'r' */
+ { 0x8A, 0x73 }, /* LATIN SMALL LETTER S WITH PALATAL HOOK -> 's' */
+ { 0x8C, 0x76 }, /* LATIN SMALL LETTER V WITH PALATAL HOOK -> 'v' */
+ { 0x8D, 0x78 }, /* LATIN SMALL LETTER X WITH PALATAL HOOK -> 'x' */
+ { 0x8E, 0x7A }, /* LATIN SMALL LETTER Z WITH PALATAL HOOK -> 'z' */
+ /* Entries for page 0x1E */
+ { 0x00, 0x41 }, /* LATIN CAPITAL LETTER A WITH RING BELOW -> 'A' */
+ { 0x01, 0x61 }, /* LATIN SMALL LETTER A WITH RING BELOW -> 'a' */
+ { 0x02, 0x42 }, /* LATIN CAPITAL LETTER B WITH DOT ABOVE -> 'B' */
+ { 0x03, 0x62 }, /* LATIN SMALL LETTER B WITH DOT ABOVE -> 'b' */
+ { 0x04, 0x42 }, /* LATIN CAPITAL LETTER B WITH DOT BELOW -> 'B' */
+ { 0x05, 0x62 }, /* LATIN SMALL LETTER B WITH DOT BELOW -> 'b' */
+ { 0x06, 0x42 }, /* LATIN CAPITAL LETTER B WITH LINE BELOW -> 'B' */
+ { 0x07, 0x62 }, /* LATIN SMALL LETTER B WITH LINE BELOW -> 'b' */
+ { 0x08, 0x43 }, /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE -> 'C' */
+ { 0x09, 0x63 }, /* LATIN SMALL LETTER C WITH CEDILLA AND ACUTE -> 'c' */
+ { 0x0A, 0x44 }, /* LATIN CAPITAL LETTER D WITH DOT ABOVE -> 'D' */
+ { 0x0B, 0x64 }, /* LATIN SMALL LETTER D WITH DOT ABOVE -> 'd' */
+ { 0x0C, 0x44 }, /* LATIN CAPITAL LETTER D WITH DOT BELOW -> 'D' */
+ { 0x0D, 0x64 }, /* LATIN SMALL LETTER D WITH DOT BELOW -> 'd' */
+ { 0x0E, 0x44 }, /* LATIN CAPITAL LETTER D WITH LINE BELOW -> 'D' */
+ { 0x0F, 0x64 }, /* LATIN SMALL LETTER D WITH LINE BELOW -> 'd' */
+ { 0x10, 0x44 }, /* LATIN CAPITAL LETTER D WITH CEDILLA -> 'D' */
+ { 0x11, 0x64 }, /* LATIN SMALL LETTER D WITH CEDILLA -> 'd' */
+ { 0x12, 0x44 }, /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW -> 'D' */
+ { 0x13, 0x64 }, /* LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW -> 'd' */
+ { 0x14, 0x45 }, /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE -> 'E' */
+ { 0x15, 0x65 }, /* LATIN SMALL LETTER E WITH MACRON AND GRAVE -> 'e' */
+ { 0x16, 0x45 }, /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE -> 'E' */
+ { 0x17, 0x65 }, /* LATIN SMALL LETTER E WITH MACRON AND ACUTE -> 'e' */
+ { 0x18, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW -> 'E' */
+ { 0x19, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW -> 'e' */
+ { 0x1A, 0x45 }, /* LATIN CAPITAL LETTER E WITH TILDE BELOW -> 'E' */
+ { 0x1B, 0x65 }, /* LATIN SMALL LETTER E WITH TILDE BELOW -> 'e' */
+ { 0x1C, 0x45 }, /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE -> 'E' */
+ { 0x1D, 0x65 }, /* LATIN SMALL LETTER E WITH CEDILLA AND BREVE -> 'e' */
+ { 0x1E, 0x46 }, /* LATIN CAPITAL LETTER F WITH DOT ABOVE -> 'F' */
+ { 0x1F, 0x66 }, /* LATIN SMALL LETTER F WITH DOT ABOVE -> 'f' */
+ { 0x20, 0x47 }, /* LATIN CAPITAL LETTER G WITH MACRON -> 'G' */
+ { 0x21, 0x67 }, /* LATIN SMALL LETTER G WITH MACRON -> 'g' */
+ { 0x22, 0x48 }, /* LATIN CAPITAL LETTER H WITH DOT ABOVE -> 'H' */
+ { 0x23, 0x68 }, /* LATIN SMALL LETTER H WITH DOT ABOVE -> 'h' */
+ { 0x24, 0x48 }, /* LATIN CAPITAL LETTER H WITH DOT BELOW -> 'H' */
+ { 0x25, 0x68 }, /* LATIN SMALL LETTER H WITH DOT BELOW -> 'h' */
+ { 0x26, 0x48 }, /* LATIN CAPITAL LETTER H WITH DIAERESIS -> 'H' */
+ { 0x27, 0x68 }, /* LATIN SMALL LETTER H WITH DIAERESIS -> 'h' */
+ { 0x28, 0x48 }, /* LATIN CAPITAL LETTER H WITH CEDILLA -> 'H' */
+ { 0x29, 0x68 }, /* LATIN SMALL LETTER H WITH CEDILLA -> 'h' */
+ { 0x2A, 0x48 }, /* LATIN CAPITAL LETTER H WITH BREVE BELOW -> 'H' */
+ { 0x2B, 0x68 }, /* LATIN SMALL LETTER H WITH BREVE BELOW -> 'h' */
+ { 0x2C, 0x49 }, /* LATIN CAPITAL LETTER I WITH TILDE BELOW -> 'I' */
+ { 0x2D, 0x69 }, /* LATIN SMALL LETTER I WITH TILDE BELOW -> 'i' */
+ { 0x2E, 0x49 }, /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE -> 'I' */
+ { 0x2F, 0x69 }, /* LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE -> 'i' */
+ { 0x30, 0x4B }, /* LATIN CAPITAL LETTER K WITH ACUTE -> 'K' */
+ { 0x31, 0x6B }, /* LATIN SMALL LETTER K WITH ACUTE -> 'k' */
+ { 0x32, 0x4B }, /* LATIN CAPITAL LETTER K WITH DOT BELOW -> 'K' */
+ { 0x33, 0x6B }, /* LATIN SMALL LETTER K WITH DOT BELOW -> 'k' */
+ { 0x34, 0x4B }, /* LATIN CAPITAL LETTER K WITH LINE BELOW -> 'K' */
+ { 0x35, 0x6B }, /* LATIN SMALL LETTER K WITH LINE BELOW -> 'k' */
+ { 0x36, 0x4C }, /* LATIN CAPITAL LETTER L WITH DOT BELOW -> 'L' */
+ { 0x37, 0x6C }, /* LATIN SMALL LETTER L WITH DOT BELOW -> 'l' */
+ { 0x38, 0x4C }, /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON -> 'L' */
+ { 0x39, 0x6C }, /* LATIN SMALL LETTER L WITH DOT BELOW AND MACRON -> 'l' */
+ { 0x3A, 0x4C }, /* LATIN CAPITAL LETTER L WITH LINE BELOW -> 'L' */
+ { 0x3B, 0x6C }, /* LATIN SMALL LETTER L WITH LINE BELOW -> 'l' */
+ { 0x3C, 0x4C }, /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW -> 'L' */
+ { 0x3D, 0x6C }, /* LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW -> 'l' */
+ { 0x3E, 0x4D }, /* LATIN CAPITAL LETTER M WITH ACUTE -> 'M' */
+ { 0x3F, 0x6D }, /* LATIN SMALL LETTER M WITH ACUTE -> 'm' */
+ { 0x40, 0x4D }, /* LATIN CAPITAL LETTER M WITH DOT ABOVE -> 'M' */
+ { 0x41, 0x6D }, /* LATIN SMALL LETTER M WITH DOT ABOVE -> 'm' */
+ { 0x42, 0x4D }, /* LATIN CAPITAL LETTER M WITH DOT BELOW -> 'M' */
+ { 0x43, 0x6D }, /* LATIN SMALL LETTER M WITH DOT BELOW -> 'm' */
+ { 0x44, 0x4E }, /* LATIN CAPITAL LETTER N WITH DOT ABOVE -> 'N' */
+ { 0x45, 0x6E }, /* LATIN SMALL LETTER N WITH DOT ABOVE -> 'n' */
+ { 0x46, 0x4E }, /* LATIN CAPITAL LETTER N WITH DOT BELOW -> 'N' */
+ { 0x47, 0x6E }, /* LATIN SMALL LETTER N WITH DOT BELOW -> 'n' */
+ { 0x48, 0x4E }, /* LATIN CAPITAL LETTER N WITH LINE BELOW -> 'N' */
+ { 0x49, 0x6E }, /* LATIN SMALL LETTER N WITH LINE BELOW -> 'n' */
+ { 0x4A, 0x4E }, /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW -> 'N' */
+ { 0x4B, 0x6E }, /* LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW -> 'n' */
+ { 0x4C, 0x4F }, /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE -> 'O' */
+ { 0x4D, 0x6F }, /* LATIN SMALL LETTER O WITH TILDE AND ACUTE -> 'o' */
+ { 0x4E, 0x4F }, /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS -> 'O' */
+ { 0x4F, 0x6F }, /* LATIN SMALL LETTER O WITH TILDE AND DIAERESIS -> 'o' */
+ { 0x50, 0x4F }, /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE -> 'O' */
+ { 0x51, 0x6F }, /* LATIN SMALL LETTER O WITH MACRON AND GRAVE -> 'o' */
+ { 0x52, 0x4F }, /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE -> 'O' */
+ { 0x53, 0x6F }, /* LATIN SMALL LETTER O WITH MACRON AND ACUTE -> 'o' */
+ { 0x54, 0x50 }, /* LATIN CAPITAL LETTER P WITH ACUTE -> 'P' */
+ { 0x55, 0x70 }, /* LATIN SMALL LETTER P WITH ACUTE -> 'p' */
+ { 0x56, 0x50 }, /* LATIN CAPITAL LETTER P WITH DOT ABOVE -> 'P' */
+ { 0x57, 0x70 }, /* LATIN SMALL LETTER P WITH DOT ABOVE -> 'p' */
+ { 0x58, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOT ABOVE -> 'R' */
+ { 0x59, 0x72 }, /* LATIN SMALL LETTER R WITH DOT ABOVE -> 'r' */
+ { 0x5A, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOT BELOW -> 'R' */
+ { 0x5B, 0x72 }, /* LATIN SMALL LETTER R WITH DOT BELOW -> 'r' */
+ { 0x5C, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON -> 'R' */
+ { 0x5D, 0x72 }, /* LATIN SMALL LETTER R WITH DOT BELOW AND MACRON -> 'r' */
+ { 0x5E, 0x52 }, /* LATIN CAPITAL LETTER R WITH LINE BELOW -> 'R' */
+ { 0x5F, 0x72 }, /* LATIN SMALL LETTER R WITH LINE BELOW -> 'r' */
+ { 0x60, 0x53 }, /* LATIN CAPITAL LETTER S WITH DOT ABOVE -> 'S' */
+ { 0x61, 0x73 }, /* LATIN SMALL LETTER S WITH DOT ABOVE -> 's' */
+ { 0x62, 0x53 }, /* LATIN CAPITAL LETTER S WITH DOT BELOW -> 'S' */
+ { 0x63, 0x73 }, /* LATIN SMALL LETTER S WITH DOT BELOW -> 's' */
+ { 0x64, 0x53 }, /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE -> 'S' */
+ { 0x65, 0x73 }, /* LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE -> 's' */
+ { 0x66, 0x53 }, /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE -> 'S' */
+ { 0x67, 0x73 }, /* LATIN SMALL LETTER S WITH CARON AND DOT ABOVE -> 's' */
+ { 0x68, 0x53 }, /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE -> 'S' */
+ { 0x69, 0x73 }, /* LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE -> 's' */
+ { 0x6A, 0x54 }, /* LATIN CAPITAL LETTER T WITH DOT ABOVE -> 'T' */
+ { 0x6B, 0x74 }, /* LATIN SMALL LETTER T WITH DOT ABOVE -> 't' */
+ { 0x6C, 0x54 }, /* LATIN CAPITAL LETTER T WITH DOT BELOW -> 'T' */
+ { 0x6D, 0x74 }, /* LATIN SMALL LETTER T WITH DOT BELOW -> 't' */
+ { 0x6E, 0x54 }, /* LATIN CAPITAL LETTER T WITH LINE BELOW -> 'T' */
+ { 0x6F, 0x74 }, /* LATIN SMALL LETTER T WITH LINE BELOW -> 't' */
+ { 0x70, 0x54 }, /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW -> 'T' */
+ { 0x71, 0x74 }, /* LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW -> 't' */
+ { 0x72, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW -> 'U' */
+ { 0x73, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS BELOW -> 'u' */
+ { 0x74, 0x55 }, /* LATIN CAPITAL LETTER U WITH TILDE BELOW -> 'U' */
+ { 0x75, 0x75 }, /* LATIN SMALL LETTER U WITH TILDE BELOW -> 'u' */
+ { 0x76, 0x55 }, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW -> 'U' */
+ { 0x77, 0x75 }, /* LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW -> 'u' */
+ { 0x78, 0x55 }, /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE -> 'U' */
+ { 0x79, 0x75 }, /* LATIN SMALL LETTER U WITH TILDE AND ACUTE -> 'u' */
+ { 0x7A, 0x55 }, /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS -> 'U' */
+ { 0x7B, 0x75 }, /* LATIN SMALL LETTER U WITH MACRON AND DIAERESIS -> 'u' */
+ { 0x7C, 0x56 }, /* LATIN CAPITAL LETTER V WITH TILDE -> 'V' */
+ { 0x7D, 0x76 }, /* LATIN SMALL LETTER V WITH TILDE -> 'v' */
+ { 0x7E, 0x56 }, /* LATIN CAPITAL LETTER V WITH DOT BELOW -> 'V' */
+ { 0x7F, 0x76 }, /* LATIN SMALL LETTER V WITH DOT BELOW -> 'v' */
+ { 0x80, 0x57 }, /* LATIN CAPITAL LETTER W WITH GRAVE -> 'W' */
+ { 0x81, 0x77 }, /* LATIN SMALL LETTER W WITH GRAVE -> 'w' */
+ { 0x82, 0x57 }, /* LATIN CAPITAL LETTER W WITH ACUTE -> 'W' */
+ { 0x83, 0x77 }, /* LATIN SMALL LETTER W WITH ACUTE -> 'w' */
+ { 0x84, 0x57 }, /* LATIN CAPITAL LETTER W WITH DIAERESIS -> 'W' */
+ { 0x85, 0x77 }, /* LATIN SMALL LETTER W WITH DIAERESIS -> 'w' */
+ { 0x86, 0x57 }, /* LATIN CAPITAL LETTER W WITH DOT ABOVE -> 'W' */
+ { 0x87, 0x77 }, /* LATIN SMALL LETTER W WITH DOT ABOVE -> 'w' */
+ { 0x88, 0x57 }, /* LATIN CAPITAL LETTER W WITH DOT BELOW -> 'W' */
+ { 0x89, 0x77 }, /* LATIN SMALL LETTER W WITH DOT BELOW -> 'w' */
+ { 0x8A, 0x58 }, /* LATIN CAPITAL LETTER X WITH DOT ABOVE -> 'X' */
+ { 0x8B, 0x78 }, /* LATIN SMALL LETTER X WITH DOT ABOVE -> 'x' */
+ { 0x8C, 0x58 }, /* LATIN CAPITAL LETTER X WITH DIAERESIS -> 'X' */
+ { 0x8D, 0x78 }, /* LATIN SMALL LETTER X WITH DIAERESIS -> 'x' */
+ { 0x8E, 0x59 }, /* LATIN CAPITAL LETTER Y WITH DOT ABOVE -> 'Y' */
+ { 0x8F, 0x79 }, /* LATIN SMALL LETTER Y WITH DOT ABOVE -> 'y' */
+ { 0x90, 0x5A }, /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX -> 'Z' */
+ { 0x91, 0x7A }, /* LATIN SMALL LETTER Z WITH CIRCUMFLEX -> 'z' */
+ { 0x92, 0x5A }, /* LATIN CAPITAL LETTER Z WITH DOT BELOW -> 'Z' */
+ { 0x93, 0x7A }, /* LATIN SMALL LETTER Z WITH DOT BELOW -> 'z' */
+ { 0x94, 0x5A }, /* LATIN CAPITAL LETTER Z WITH LINE BELOW -> 'Z' */
+ { 0x95, 0x7A }, /* LATIN SMALL LETTER Z WITH LINE BELOW -> 'z' */
+ { 0x96, 0x68 }, /* LATIN SMALL LETTER H WITH LINE BELOW -> 'h' */
+ { 0x97, 0x74 }, /* LATIN SMALL LETTER T WITH DIAERESIS -> 't' */
+ { 0x98, 0x77 }, /* LATIN SMALL LETTER W WITH RING ABOVE -> 'w' */
+ { 0x99, 0x79 }, /* LATIN SMALL LETTER Y WITH RING ABOVE -> 'y' */
+ { 0x9A, 0x61 }, /* LATIN SMALL LETTER A WITH RIGHT HALF RING -> 'a' */
+ { 0x9B, 0x53 }, /* LATIN SMALL LETTER LONG S WITH DOT ABOVE -> 'S' */
+ { 0xA0, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOT BELOW -> 'A' */
+ { 0xA1, 0x61 }, /* LATIN SMALL LETTER A WITH DOT BELOW -> 'a' */
+ { 0xA2, 0x41 }, /* LATIN CAPITAL LETTER A WITH HOOK ABOVE -> 'A' */
+ { 0xA3, 0x61 }, /* LATIN SMALL LETTER A WITH HOOK ABOVE -> 'a' */
+ { 0xA4, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE -> 'A' */
+ { 0xA5, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE -> 'a' */
+ { 0xA6, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE -> 'A' */
+ { 0xA7, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE -> 'a' */
+ { 0xA8, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE -> 'A' */
+ { 0xA9, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE -> 'a' */
+ { 0xAA, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE -> 'A' */
+ { 0xAB, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE -> 'a' */
+ { 0xAC, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW -> 'A' */
+ { 0xAD, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW -> 'a' */
+ { 0xAE, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE -> 'A' */
+ { 0xAF, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND ACUTE -> 'a' */
+ { 0xB0, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE -> 'A' */
+ { 0xB1, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND GRAVE -> 'a' */
+ { 0xB2, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE -> 'A' */
+ { 0xB3, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE -> 'a' */
+ { 0xB4, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE -> 'A' */
+ { 0xB5, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND TILDE -> 'a' */
+ { 0xB6, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW -> 'A' */
+ { 0xB7, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND DOT BELOW -> 'a' */
+ { 0xB8, 0x45 }, /* LATIN CAPITAL LETTER E WITH DOT BELOW -> 'E' */
+ { 0xB9, 0x65 }, /* LATIN SMALL LETTER E WITH DOT BELOW -> 'e' */
+ { 0xBA, 0x45 }, /* LATIN CAPITAL LETTER E WITH HOOK ABOVE -> 'E' */
+ { 0xBB, 0x65 }, /* LATIN SMALL LETTER E WITH HOOK ABOVE -> 'e' */
+ { 0xBC, 0x45 }, /* LATIN CAPITAL LETTER E WITH TILDE -> 'E' */
+ { 0xBD, 0x65 }, /* LATIN SMALL LETTER E WITH TILDE -> 'e' */
+ { 0xBE, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE -> 'E' */
+ { 0xBF, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE -> 'e' */
+ { 0xC0, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE -> 'E' */
+ { 0xC1, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE -> 'e' */
+ { 0xC2, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE -> 'E' */
+ { 0xC3, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE -> 'e' */
+ { 0xC4, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE -> 'E' */
+ { 0xC5, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE -> 'e' */
+ { 0xC6, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW -> 'E' */
+ { 0xC7, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW -> 'e' */
+ { 0xC8, 0x49 }, /* LATIN CAPITAL LETTER I WITH HOOK ABOVE -> 'I' */
+ { 0xC9, 0x69 }, /* LATIN SMALL LETTER I WITH HOOK ABOVE -> 'i' */
+ { 0xCA, 0x49 }, /* LATIN CAPITAL LETTER I WITH DOT BELOW -> 'I' */
+ { 0xCB, 0x69 }, /* LATIN SMALL LETTER I WITH DOT BELOW -> 'i' */
+ { 0xCC, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOT BELOW -> 'O' */
+ { 0xCD, 0x6F }, /* LATIN SMALL LETTER O WITH DOT BELOW -> 'o' */
+ { 0xCE, 0x4F }, /* LATIN CAPITAL LETTER O WITH HOOK ABOVE -> 'O' */
+ { 0xCF, 0x6F }, /* LATIN SMALL LETTER O WITH HOOK ABOVE -> 'o' */
+ { 0xD0, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE -> 'O' */
+ { 0xD1, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE -> 'o' */
+ { 0xD2, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE -> 'O' */
+ { 0xD3, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE -> 'o' */
+ { 0xD4, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE -> 'O' */
+ { 0xD5, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE -> 'o' */
+ { 0xD6, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE -> 'O' */
+ { 0xD7, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE -> 'o' */
+ { 0xD8, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW -> 'O' */
+ { 0xD9, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW -> 'o' */
+ { 0xDA, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE -> 'O' */
+ { 0xDB, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND ACUTE -> 'o' */
+ { 0xDC, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE -> 'O' */
+ { 0xDD, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND GRAVE -> 'o' */
+ { 0xDE, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE -> 'O' */
+ { 0xDF, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE -> 'o' */
+ { 0xE0, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND TILDE -> 'O' */
+ { 0xE1, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND TILDE -> 'o' */
+ { 0xE2, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW -> 'O' */
+ { 0xE3, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND DOT BELOW -> 'o' */
+ { 0xE4, 0x55 }, /* LATIN CAPITAL LETTER U WITH DOT BELOW -> 'U' */
+ { 0xE5, 0x75 }, /* LATIN SMALL LETTER U WITH DOT BELOW -> 'u' */
+ { 0xE6, 0x55 }, /* LATIN CAPITAL LETTER U WITH HOOK ABOVE -> 'U' */
+ { 0xE7, 0x75 }, /* LATIN SMALL LETTER U WITH HOOK ABOVE -> 'u' */
+ { 0xE8, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE -> 'U' */
+ { 0xE9, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND ACUTE -> 'u' */
+ { 0xEA, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE -> 'U' */
+ { 0xEB, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND GRAVE -> 'u' */
+ { 0xEC, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE -> 'U' */
+ { 0xED, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE -> 'u' */
+ { 0xEE, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND TILDE -> 'U' */
+ { 0xEF, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND TILDE -> 'u' */
+ { 0xF0, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW -> 'U' */
+ { 0xF1, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND DOT BELOW -> 'u' */
+ { 0xF2, 0x59 }, /* LATIN CAPITAL LETTER Y WITH GRAVE -> 'Y' */
+ { 0xF3, 0x79 }, /* LATIN SMALL LETTER Y WITH GRAVE -> 'y' */
+ { 0xF4, 0x59 }, /* LATIN CAPITAL LETTER Y WITH DOT BELOW -> 'Y' */
+ { 0xF5, 0x79 }, /* LATIN SMALL LETTER Y WITH DOT BELOW -> 'y' */
+ { 0xF6, 0x59 }, /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE -> 'Y' */
+ { 0xF7, 0x79 }, /* LATIN SMALL LETTER Y WITH HOOK ABOVE -> 'y' */
+ { 0xF8, 0x59 }, /* LATIN CAPITAL LETTER Y WITH TILDE -> 'Y' */
+ { 0xF9, 0x79 }, /* LATIN SMALL LETTER Y WITH TILDE -> 'y' */
+ /* Entries for page 0x1F */
+ { 0x00, 0x00 }, /* GREEK SMALL LETTER ALPHA WITH PSILI -> ... */
+ { 0x07, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI -> 'a' */
+ { 0x08, 0x00 }, /* GREEK CAPITAL LETTER ALPHA WITH PSILI -> ... */
+ { 0x0F, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI -> 'A' */
+ { 0x10, 0x00 }, /* GREEK SMALL LETTER EPSILON WITH PSILI -> ... */
+ { 0x15, 0x65 }, /* GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA -> 'e' */
+ { 0x18, 0x00 }, /* GREEK CAPITAL LETTER EPSILON WITH PSILI -> ... */
+ { 0x1D, 0x45 }, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA -> 'E' */
+ { 0x20, 0x00 }, /* GREEK SMALL LETTER ETA WITH PSILI -> ... */
+ { 0x27, 0x65 }, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI -> 'e' */
+ { 0x28, 0x00 }, /* GREEK CAPITAL LETTER ETA WITH PSILI -> ... */
+ { 0x2F, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI -> 'E' */
+ { 0x30, 0x00 }, /* GREEK SMALL LETTER IOTA WITH PSILI -> ... */
+ { 0x37, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI -> 'i' */
+ { 0x38, 0x00 }, /* GREEK CAPITAL LETTER IOTA WITH PSILI -> ... */
+ { 0x3F, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI -> 'I' */
+ { 0x40, 0x00 }, /* GREEK SMALL LETTER OMICRON WITH PSILI -> ... */
+ { 0x45, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA -> 'o' */
+ { 0x48, 0x00 }, /* GREEK CAPITAL LETTER OMICRON WITH PSILI -> ... */
+ { 0x4D, 0x4F }, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA -> 'O' */
+ { 0x50, 0x00 }, /* GREEK SMALL LETTER UPSILON WITH PSILI -> ... */
+ { 0x57, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI -> 'u' */
+ { 0x59, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA -> 'U' */
+ { 0x5B, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA -> 'U' */
+ { 0x5D, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA -> 'U' */
+ { 0x5F, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI -> 'U' */
+ { 0x60, 0x00 }, /* GREEK SMALL LETTER OMEGA WITH PSILI -> ... */
+ { 0x67, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI -> 'o' */
+ { 0x68, 0x00 }, /* GREEK CAPITAL LETTER OMEGA WITH PSILI -> ... */
+ { 0x6F, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI -> 'O' */
+ { 0x70, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH VARIA -> 'a' */
+ { 0x71, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH OXIA -> 'a' */
+ { 0x72, 0x00 }, /* GREEK SMALL LETTER EPSILON WITH VARIA -> ... */
+ { 0x75, 0x65 }, /* GREEK SMALL LETTER ETA WITH OXIA -> 'e' */
+ { 0x76, 0x69 }, /* GREEK SMALL LETTER IOTA WITH VARIA -> 'i' */
+ { 0x77, 0x69 }, /* GREEK SMALL LETTER IOTA WITH OXIA -> 'i' */
+ { 0x78, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH VARIA -> 'o' */
+ { 0x79, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH OXIA -> 'o' */
+ { 0x7A, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH VARIA -> 'u' */
+ { 0x7B, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH OXIA -> 'u' */
+ { 0x7C, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH VARIA -> 'o' */
+ { 0x7D, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH OXIA -> 'o' */
+ { 0x80, 0x00 }, /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI -> ... */
+ { 0x87, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI -> 'a' */
+ { 0x88, 0x00 }, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI -> ... */
+ { 0x8F, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI -> 'A' */
+ { 0x90, 0x00 }, /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI -> ... */
+ { 0x97, 0x65 }, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI -> 'e' */
+ { 0x98, 0x00 }, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI -> ... */
+ { 0x9F, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI -> 'E' */
+ { 0xA0, 0x00 }, /* GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI -> ... */
+ { 0xA7, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI -> 'o' */
+ { 0xA8, 0x00 }, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI -> ... */
+ { 0xAF, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI -> 'O' */
+ { 0xB0, 0x00 }, /* GREEK SMALL LETTER ALPHA WITH VRACHY -> ... */
+ { 0xB4, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI -> 'a' */
+ { 0xB6, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI -> 'a' */
+ { 0xB7, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI -> 'a' */
+ { 0xB8, 0x00 }, /* GREEK CAPITAL LETTER ALPHA WITH VRACHY -> ... */
+ { 0xBC, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI -> 'A' */
+ { 0xBD, 0x27 }, /* GREEK KORONIS -> ''' */
+ { 0xBE, 0x69 }, /* GREEK PROSGEGRAMMENI -> 'i' */
+ { 0xBF, 0x27 }, /* GREEK PSILI -> ''' */
+ { 0xC0, 0x7E }, /* GREEK PERISPOMENI -> '~' */
+ { 0xC2, 0x00 }, /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI -> ... */
+ { 0xC4, 0x65 }, /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI -> 'e' */
+ { 0xC6, 0x65 }, /* GREEK SMALL LETTER ETA WITH PERISPOMENI -> 'e' */
+ { 0xC7, 0x65 }, /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI -> 'e' */
+ { 0xC8, 0x00 }, /* GREEK CAPITAL LETTER EPSILON WITH VARIA -> ... */
+ { 0xCC, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI -> 'E' */
+ { 0xD0, 0x00 }, /* GREEK SMALL LETTER IOTA WITH VRACHY -> ... */
+ { 0xD3, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA -> 'i' */
+ { 0xD6, 0x69 }, /* GREEK SMALL LETTER IOTA WITH PERISPOMENI -> 'i' */
+ { 0xD7, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI -> 'i' */
+ { 0xD8, 0x00 }, /* GREEK CAPITAL LETTER IOTA WITH VRACHY -> ... */
+ { 0xDB, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH OXIA -> 'I' */
+ { 0xE0, 0x00 }, /* GREEK SMALL LETTER UPSILON WITH VRACHY -> ... */
+ { 0xE3, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA -> 'u' */
+ { 0xE4, 0x52 }, /* GREEK SMALL LETTER RHO WITH PSILI -> 'R' */
+ { 0xE5, 0x52 }, /* GREEK SMALL LETTER RHO WITH DASIA -> 'R' */
+ { 0xE6, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI -> 'u' */
+ { 0xE7, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI -> 'u' */
+ { 0xE8, 0x00 }, /* GREEK CAPITAL LETTER UPSILON WITH VRACHY -> ... */
+ { 0xEB, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH OXIA -> 'U' */
+ { 0xEC, 0x52 }, /* GREEK CAPITAL LETTER RHO WITH DASIA -> 'R' */
+ { 0xEF, 0x60 }, /* GREEK VARIA -> '`' */
+ { 0xF2, 0x00 }, /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI -> ... */
+ { 0xF4, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI -> 'o' */
+ { 0xF6, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI -> 'o' */
+ { 0xF7, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI -> 'o' */
+ { 0xF8, 0x00 }, /* GREEK CAPITAL LETTER OMICRON WITH VARIA -> ... */
+ { 0xFC, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI -> 'O' */
+ { 0xFD, 0x27 }, /* GREEK OXIA -> ''' */
+ { 0xFE, 0x60 }, /* GREEK DASIA -> '`' */
+ /* Entries for page 0x20 */
+ { 0x00, 0x00 }, /* EN QUAD -> ... */
+ { 0x0B, 0x20 }, /* ZERO WIDTH SPACE -> ' ' */
+ { 0x10, 0x00 }, /* HYPHEN -> ... */
+ { 0x15, 0x2D }, /* HORIZONTAL BAR -> '-' */
+ { 0x17, 0x5F }, /* DOUBLE LOW LINE -> '_' */
+ { 0x18, 0x27 }, /* LEFT SINGLE QUOTATION MARK -> ''' */
+ { 0x19, 0x27 }, /* RIGHT SINGLE QUOTATION MARK -> ''' */
+ { 0x1A, 0x2C }, /* SINGLE LOW-9 QUOTATION MARK -> ',' */
+ { 0x1B, 0x27 }, /* SINGLE HIGH-REVERSED-9 QUOTATION MARK -> ''' */
+ { 0x1C, 0x00 }, /* LEFT DOUBLE QUOTATION MARK -> ... */
+ { 0x1F, 0x22 }, /* DOUBLE HIGH-REVERSED-9 QUOTATION MARK -> '"' */
+ { 0x20, 0x2B }, /* DAGGER -> '+' */
+ { 0x22, 0x2A }, /* BULLET -> '*' */
+ { 0x23, 0x3E }, /* TRIANGULAR BULLET -> '>' */
+ { 0x24, 0x2E }, /* ONE DOT LEADER -> '.' */
+ { 0x26, 0x2E }, /* HORIZONTAL ELLIPSIS -> '.' */
+ { 0x27, 0x2E }, /* HYPHENATION POINT -> '.' */
+ { 0x2F, 0x20 }, /* NARROW NO-BREAK SPACE -> ' ' */
+ { 0x32, 0x27 }, /* PRIME -> ''' */
+ { 0x33, 0x22 }, /* DOUBLE PRIME -> '"' */
+ { 0x35, 0x60 }, /* REVERSED PRIME -> '`' */
+ { 0x38, 0x5E }, /* CARET -> '^' */
+ { 0x39, 0x3C }, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK -> '<' */
+ { 0x3A, 0x3E }, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -> '>' */
+ { 0x3B, 0x2A }, /* REFERENCE MARK -> '*' */
+ { 0x3C, 0x21 }, /* DOUBLE EXCLAMATION MARK -> '!' */
+ { 0x3D, 0x3F }, /* INTERROBANG -> '?' */
+ { 0x3E, 0x2D }, /* OVERLINE -> '-' */
+ { 0x3F, 0x5F }, /* UNDERTIE -> '_' */
+ { 0x40, 0x2D }, /* CHARACTER TIE -> '-' */
+ { 0x41, 0x5E }, /* CARET INSERTION POINT -> '^' */
+ { 0x42, 0x2A }, /* ASTERISM -> '*' */
+ { 0x43, 0x2D }, /* HYPHEN BULLET -> '-' */
+ { 0x44, 0x2F }, /* FRACTION SLASH -> '/' */
+ { 0x47, 0x3F }, /* DOUBLE QUESTION MARK -> '?' */
+ { 0x48, 0x3F }, /* QUESTION EXCLAMATION MARK -> '?' */
+ { 0x49, 0x21 }, /* EXCLAMATION QUESTION MARK -> '!' */
+ { 0x4A, 0x26 }, /* TIRONIAN SIGN ET -> '&' */
+ { 0x4B, 0x50 }, /* REVERSED PILCROW SIGN -> 'P' */
+ { 0x4C, 0x3C }, /* BLACK LEFTWARDS BULLET -> '<' */
+ { 0x4D, 0x3E }, /* BLACK RIGHTWARDS BULLET -> '>' */
+ { 0x4E, 0x2A }, /* LOW ASTERISK -> '*' */
+ { 0x4F, 0x3B }, /* REVERSED SEMICOLON -> ';' */
+ { 0x51, 0x2A }, /* TWO ASTERISKS ALIGNED VERTICALLY -> '*' */
+ { 0x52, 0x2D }, /* COMMERCIAL MINUS SIGN -> '-' */
+ { 0x53, 0x7E }, /* SWUNG DASH -> '~' */
+ { 0x55, 0x2A }, /* FLOWER PUNCTUATION MARK -> '*' */
+ { 0x5B, 0x3A }, /* FOUR DOT MARK -> ':' */
+ { 0x5F, 0x20 }, /* MEDIUM MATHEMATICAL SPACE -> ' ' */
+ { 0x70, 0x30 }, /* SUPERSCRIPT ZERO -> '0' */
+ { 0x71, 0x69 }, /* SUPERSCRIPT LATIN SMALL LETTER I -> 'i' */
+ { 0x74, 0x34 }, /* SUPERSCRIPT FOUR -> '4' */
+ { 0x75, 0x35 }, /* SUPERSCRIPT FIVE -> '5' */
+ { 0x76, 0x36 }, /* SUPERSCRIPT SIX -> '6' */
+ { 0x77, 0x37 }, /* SUPERSCRIPT SEVEN -> '7' */
+ { 0x78, 0x38 }, /* SUPERSCRIPT EIGHT -> '8' */
+ { 0x79, 0x39 }, /* SUPERSCRIPT NINE -> '9' */
+ { 0x7A, 0x2B }, /* SUPERSCRIPT PLUS SIGN -> '+' */
+ { 0x7B, 0x2D }, /* SUPERSCRIPT MINUS -> '-' */
+ { 0x7C, 0x3D }, /* SUPERSCRIPT EQUALS SIGN -> '=' */
+ { 0x7D, 0x28 }, /* SUPERSCRIPT LEFT PARENTHESIS -> '(' */
+ { 0x7E, 0x29 }, /* SUPERSCRIPT RIGHT PARENTHESIS -> ')' */
+ { 0x7F, 0x6E }, /* SUPERSCRIPT LATIN SMALL LETTER N -> 'n' */
+ { 0x80, 0x30 }, /* SUBSCRIPT ZERO -> '0' */
+ { 0x81, 0x31 }, /* SUBSCRIPT ONE -> '1' */
+ { 0x82, 0x32 }, /* SUBSCRIPT TWO -> '2' */
+ { 0x83, 0x33 }, /* SUBSCRIPT THREE -> '3' */
+ { 0x84, 0x34 }, /* SUBSCRIPT FOUR -> '4' */
+ { 0x85, 0x35 }, /* SUBSCRIPT FIVE -> '5' */
+ { 0x86, 0x36 }, /* SUBSCRIPT SIX -> '6' */
+ { 0x87, 0x37 }, /* SUBSCRIPT SEVEN -> '7' */
+ { 0x88, 0x38 }, /* SUBSCRIPT EIGHT -> '8' */
+ { 0x89, 0x39 }, /* SUBSCRIPT NINE -> '9' */
+ { 0x8A, 0x2B }, /* SUBSCRIPT PLUS SIGN -> '+' */
+ { 0x8B, 0x2D }, /* SUBSCRIPT MINUS -> '-' */
+ { 0x8C, 0x3D }, /* SUBSCRIPT EQUALS SIGN -> '=' */
+ { 0x8D, 0x28 }, /* SUBSCRIPT LEFT PARENTHESIS -> '(' */
+ { 0x8E, 0x29 }, /* SUBSCRIPT RIGHT PARENTHESIS -> ')' */
+ { 0x90, 0x61 }, /* LATIN SUBSCRIPT SMALL LETTER A -> 'a' */
+ { 0x91, 0x65 }, /* LATIN SUBSCRIPT SMALL LETTER E -> 'e' */
+ { 0x92, 0x6F }, /* LATIN SUBSCRIPT SMALL LETTER O -> 'o' */
+ { 0x93, 0x78 }, /* LATIN SUBSCRIPT SMALL LETTER X -> 'x' */
+ { 0x95, 0x68 }, /* LATIN SUBSCRIPT SMALL LETTER H -> 'h' */
+ { 0x96, 0x6B }, /* LATIN SUBSCRIPT SMALL LETTER K -> 'k' */
+ { 0x97, 0x6C }, /* LATIN SUBSCRIPT SMALL LETTER L -> 'l' */
+ { 0x98, 0x6D }, /* LATIN SUBSCRIPT SMALL LETTER M -> 'm' */
+ { 0x99, 0x6E }, /* LATIN SUBSCRIPT SMALL LETTER N -> 'n' */
+ { 0x9A, 0x70 }, /* LATIN SUBSCRIPT SMALL LETTER P -> 'p' */
+ { 0x9B, 0x73 }, /* LATIN SUBSCRIPT SMALL LETTER S -> 's' */
+ { 0x9C, 0x74 }, /* LATIN SUBSCRIPT SMALL LETTER T -> 't' */
+ { 0xA4, 0x4C }, /* LIRA SIGN -> 'L' */
+ { 0xA6, 0x4E }, /* NAIRA SIGN -> 'N' */
+ { 0xA9, 0x57 }, /* WON SIGN -> 'W' */
+ { 0xAB, 0x44 }, /* DONG SIGN -> 'D' */
+ { 0xAC, 0x45 }, /* EURO SIGN -> 'E' */
+ { 0xAD, 0x4B }, /* KIP SIGN -> 'K' */
+ { 0xAE, 0x54 }, /* TUGRIK SIGN -> 'T' */
+ { 0xB1, 0x50 }, /* PESO SIGN -> 'P' */
+ { 0xB2, 0x47 }, /* GUARANI SIGN -> 'G' */
+ { 0xB3, 0x41 }, /* AUSTRAL SIGN -> 'A' */
+ { 0xB6, 0x4C }, /* LIVRE TOURNOIS SIGN -> 'L' */
+ { 0xB8, 0x54 }, /* TENGE SIGN -> 'T' */
+ { 0xBA, 0x4C }, /* TURKISH LIRA SIGN -> 'L' */
+ { 0xBB, 0x4D }, /* NORDIC MARK SIGN -> 'M' */
+ { 0xBC, 0x6D }, /* MANAT SIGN -> 'm' */
+ { 0xBD, 0x52 }, /* RUBLE SIGN -> 'R' */
+ { 0xBE, 0x6C }, /* LARI SIGN -> 'l' */
+ /* Entries for page 0x21 */
+ { 0x02, 0x43 }, /* DOUBLE-STRUCK CAPITAL C -> 'C' */
+ { 0x03, 0x43 }, /* DEGREE CELSIUS -> 'C' */
+ { 0x09, 0x46 }, /* DEGREE FAHRENHEIT -> 'F' */
+ { 0x0A, 0x67 }, /* SCRIPT SMALL G -> 'g' */
+ { 0x0B, 0x00 }, /* SCRIPT CAPITAL H -> ... */
+ { 0x0D, 0x48 }, /* DOUBLE-STRUCK CAPITAL H -> 'H' */
+ { 0x0E, 0x68 }, /* PLANCK CONSTANT -> 'h' */
+ { 0x10, 0x49 }, /* SCRIPT CAPITAL I -> 'I' */
+ { 0x11, 0x49 }, /* BLACK-LETTER CAPITAL I -> 'I' */
+ { 0x12, 0x4C }, /* SCRIPT CAPITAL L -> 'L' */
+ { 0x13, 0x6C }, /* SCRIPT SMALL L -> 'l' */
+ { 0x15, 0x4E }, /* DOUBLE-STRUCK CAPITAL N -> 'N' */
+ { 0x19, 0x50 }, /* DOUBLE-STRUCK CAPITAL P -> 'P' */
+ { 0x1A, 0x51 }, /* DOUBLE-STRUCK CAPITAL Q -> 'Q' */
+ { 0x1B, 0x00 }, /* SCRIPT CAPITAL R -> ... */
+ { 0x1D, 0x52 }, /* DOUBLE-STRUCK CAPITAL R -> 'R' */
+ { 0x22, 0x54 }, /* TRADE MARK SIGN -> 'T' */
+ { 0x24, 0x5A }, /* DOUBLE-STRUCK CAPITAL Z -> 'Z' */
+ { 0x28, 0x5A }, /* BLACK-LETTER CAPITAL Z -> 'Z' */
+ { 0x2A, 0x4B }, /* KELVIN SIGN -> 'K' */
+ { 0x2B, 0x41 }, /* ANGSTROM SIGN -> 'A' */
+ { 0x2C, 0x42 }, /* SCRIPT CAPITAL B -> 'B' */
+ { 0x2D, 0x43 }, /* BLACK-LETTER CAPITAL C -> 'C' */
+ { 0x2E, 0x65 }, /* ESTIMATED SYMBOL -> 'e' */
+ { 0x2F, 0x65 }, /* SCRIPT SMALL E -> 'e' */
+ { 0x30, 0x45 }, /* SCRIPT CAPITAL E -> 'E' */
+ { 0x31, 0x46 }, /* SCRIPT CAPITAL F -> 'F' */
+ { 0x32, 0x46 }, /* TURNED CAPITAL F -> 'F' */
+ { 0x33, 0x4D }, /* SCRIPT CAPITAL M -> 'M' */
+ { 0x34, 0x6F }, /* SCRIPT SMALL O -> 'o' */
+ { 0x39, 0x69 }, /* INFORMATION SOURCE -> 'i' */
+ { 0x45, 0x44 }, /* DOUBLE-STRUCK ITALIC CAPITAL D -> 'D' */
+ { 0x46, 0x64 }, /* DOUBLE-STRUCK ITALIC SMALL D -> 'd' */
+ { 0x47, 0x65 }, /* DOUBLE-STRUCK ITALIC SMALL E -> 'e' */
+ { 0x48, 0x69 }, /* DOUBLE-STRUCK ITALIC SMALL I -> 'i' */
+ { 0x49, 0x6A }, /* DOUBLE-STRUCK ITALIC SMALL J -> 'j' */
+ { 0x4E, 0x46 }, /* TURNED SMALL F -> 'F' */
+ { 0x60, 0x49 }, /* ROMAN NUMERAL ONE -> 'I' */
+ { 0x64, 0x56 }, /* ROMAN NUMERAL FIVE -> 'V' */
+ { 0x69, 0x58 }, /* ROMAN NUMERAL TEN -> 'X' */
+ { 0x6C, 0x4C }, /* ROMAN NUMERAL FIFTY -> 'L' */
+ { 0x6D, 0x43 }, /* ROMAN NUMERAL ONE HUNDRED -> 'C' */
+ { 0x6E, 0x44 }, /* ROMAN NUMERAL FIVE HUNDRED -> 'D' */
+ { 0x6F, 0x4D }, /* ROMAN NUMERAL ONE THOUSAND -> 'M' */
+ { 0x70, 0x69 }, /* SMALL ROMAN NUMERAL ONE -> 'i' */
+ { 0x74, 0x76 }, /* SMALL ROMAN NUMERAL FIVE -> 'v' */
+ { 0x79, 0x78 }, /* SMALL ROMAN NUMERAL TEN -> 'x' */
+ { 0x7C, 0x6C }, /* SMALL ROMAN NUMERAL FIFTY -> 'l' */
+ { 0x7D, 0x63 }, /* SMALL ROMAN NUMERAL ONE HUNDRED -> 'c' */
+ { 0x7E, 0x64 }, /* SMALL ROMAN NUMERAL FIVE HUNDRED -> 'd' */
+ { 0x7F, 0x6D }, /* SMALL ROMAN NUMERAL ONE THOUSAND -> 'm' */
+ { 0x83, 0x29 }, /* ROMAN NUMERAL REVERSED ONE HUNDRED -> ')' */
+ { 0x90, 0x3C }, /* LEFTWARDS ARROW -> '<' */
+ { 0x91, 0x5E }, /* UPWARDS ARROW -> '^' */
+ { 0x92, 0x3E }, /* RIGHTWARDS ARROW -> '>' */
+ { 0x93, 0x76 }, /* DOWNWARDS ARROW -> 'v' */
+ { 0x94, 0x2D }, /* LEFT RIGHT ARROW -> '-' */
+ { 0x95, 0x7C }, /* UP DOWN ARROW -> '|' */
+ { 0x96, 0x5C }, /* NORTH WEST ARROW -> '\' */
+ { 0x97, 0x2F }, /* NORTH EAST ARROW -> '/' */
+ { 0x98, 0x5C }, /* SOUTH EAST ARROW -> '\' */
+ { 0x99, 0x2F }, /* SOUTH WEST ARROW -> '/' */
+ { 0x9A, 0x21 }, /* LEFTWARDS ARROW WITH STROKE -> '!' */
+ { 0x9B, 0x21 }, /* RIGHTWARDS ARROW WITH STROKE -> '!' */
+ { 0x9C, 0x7E }, /* LEFTWARDS WAVE ARROW -> '~' */
+ { 0x9D, 0x7E }, /* RIGHTWARDS WAVE ARROW -> '~' */
+ { 0x9E, 0x2D }, /* LEFTWARDS TWO HEADED ARROW -> '-' */
+ { 0x9F, 0x7C }, /* UPWARDS TWO HEADED ARROW -> '|' */
+ { 0xA0, 0x2D }, /* RIGHTWARDS TWO HEADED ARROW -> '-' */
+ { 0xA1, 0x7C }, /* DOWNWARDS TWO HEADED ARROW -> '|' */
+ { 0xA2, 0x00 }, /* LEFTWARDS ARROW WITH TAIL -> ... */
+ { 0xA4, 0x2D }, /* LEFTWARDS ARROW FROM BAR -> '-' */
+ { 0xA5, 0x7C }, /* UPWARDS ARROW FROM BAR -> '|' */
+ { 0xA6, 0x2D }, /* RIGHTWARDS ARROW FROM BAR -> '-' */
+ { 0xA7, 0x7C }, /* DOWNWARDS ARROW FROM BAR -> '|' */
+ { 0xA8, 0x7C }, /* UP DOWN ARROW WITH BASE -> '|' */
+ { 0xA9, 0x00 }, /* LEFTWARDS ARROW WITH HOOK -> ... */
+ { 0xAD, 0x2D }, /* LEFT RIGHT WAVE ARROW -> '-' */
+ { 0xAE, 0x21 }, /* LEFT RIGHT ARROW WITH STROKE -> '!' */
+ { 0xAF, 0x00 }, /* DOWNWARDS ZIGZAG ARROW -> ... */
+ { 0xB5, 0x7C }, /* DOWNWARDS ARROW WITH CORNER LEFTWARDS -> '|' */
+ { 0xB6, 0x5E }, /* ANTICLOCKWISE TOP SEMICIRCLE ARROW -> '^' */
+ { 0xB7, 0x56 }, /* CLOCKWISE TOP SEMICIRCLE ARROW -> 'V' */
+ { 0xB8, 0x5C }, /* NORTH WEST ARROW TO LONG BAR -> '\' */
+ { 0xB9, 0x3D }, /* LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR -> '=' */
+ { 0xBA, 0x56 }, /* ANTICLOCKWISE OPEN CIRCLE ARROW -> 'V' */
+ { 0xBB, 0x5E }, /* CLOCKWISE OPEN CIRCLE ARROW -> '^' */
+ { 0xBC, 0x2D }, /* LEFTWARDS HARPOON WITH BARB UPWARDS -> '-' */
+ { 0xBD, 0x2D }, /* LEFTWARDS HARPOON WITH BARB DOWNWARDS -> '-' */
+ { 0xBE, 0x7C }, /* UPWARDS HARPOON WITH BARB RIGHTWARDS -> '|' */
+ { 0xBF, 0x7C }, /* UPWARDS HARPOON WITH BARB LEFTWARDS -> '|' */
+ { 0xC0, 0x2D }, /* RIGHTWARDS HARPOON WITH BARB UPWARDS -> '-' */
+ { 0xC1, 0x2D }, /* RIGHTWARDS HARPOON WITH BARB DOWNWARDS -> '-' */
+ { 0xC2, 0x7C }, /* DOWNWARDS HARPOON WITH BARB RIGHTWARDS -> '|' */
+ { 0xC3, 0x7C }, /* DOWNWARDS HARPOON WITH BARB LEFTWARDS -> '|' */
+ { 0xC4, 0x3D }, /* RIGHTWARDS ARROW OVER LEFTWARDS ARROW -> '=' */
+ { 0xC5, 0x7C }, /* UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW -> '|' */
+ { 0xC6, 0x3D }, /* LEFTWARDS ARROW OVER RIGHTWARDS ARROW -> '=' */
+ { 0xC7, 0x3D }, /* LEFTWARDS PAIRED ARROWS -> '=' */
+ { 0xC8, 0x7C }, /* UPWARDS PAIRED ARROWS -> '|' */
+ { 0xC9, 0x3D }, /* RIGHTWARDS PAIRED ARROWS -> '=' */
+ { 0xCA, 0x7C }, /* DOWNWARDS PAIRED ARROWS -> '|' */
+ { 0xCB, 0x3D }, /* LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON -> '=' */
+ { 0xCC, 0x3D }, /* RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON -> '=' */
+ { 0xCD, 0x00 }, /* LEFTWARDS DOUBLE ARROW WITH STROKE -> ... */
+ { 0xCF, 0x21 }, /* RIGHTWARDS DOUBLE ARROW WITH STROKE -> '!' */
+ { 0xD0, 0x3C }, /* LEFTWARDS DOUBLE ARROW -> '<' */
+ { 0xD1, 0x5E }, /* UPWARDS DOUBLE ARROW -> '^' */
+ { 0xD2, 0x3E }, /* RIGHTWARDS DOUBLE ARROW -> '>' */
+ { 0xD3, 0x76 }, /* DOWNWARDS DOUBLE ARROW -> 'v' */
+ { 0xD4, 0x3D }, /* LEFT RIGHT DOUBLE ARROW -> '=' */
+ { 0xD5, 0x7C }, /* UP DOWN DOUBLE ARROW -> '|' */
+ { 0xD6, 0x5C }, /* NORTH WEST DOUBLE ARROW -> '\' */
+ { 0xD7, 0x2F }, /* NORTH EAST DOUBLE ARROW -> '/' */
+ { 0xD8, 0x5C }, /* SOUTH EAST DOUBLE ARROW -> '\' */
+ { 0xD9, 0x2F }, /* SOUTH WEST DOUBLE ARROW -> '/' */
+ { 0xDA, 0x3D }, /* LEFTWARDS TRIPLE ARROW -> '=' */
+ { 0xDB, 0x3D }, /* RIGHTWARDS TRIPLE ARROW -> '=' */
+ { 0xDC, 0x7E }, /* LEFTWARDS SQUIGGLE ARROW -> '~' */
+ { 0xDD, 0x7E }, /* RIGHTWARDS SQUIGGLE ARROW -> '~' */
+ { 0xDE, 0x7C }, /* UPWARDS ARROW WITH DOUBLE STROKE -> '|' */
+ { 0xDF, 0x7C }, /* DOWNWARDS ARROW WITH DOUBLE STROKE -> '|' */
+ { 0xE0, 0x2D }, /* LEFTWARDS DASHED ARROW -> '-' */
+ { 0xE1, 0x7C }, /* UPWARDS DASHED ARROW -> '|' */
+ { 0xE2, 0x2D }, /* RIGHTWARDS DASHED ARROW -> '-' */
+ { 0xE3, 0x7C }, /* DOWNWARDS DASHED ARROW -> '|' */
+ { 0xE4, 0x00 }, /* LEFTWARDS ARROW TO BAR -> ... */
+ { 0xE6, 0x2D }, /* LEFTWARDS WHITE ARROW -> '-' */
+ { 0xE7, 0x7C }, /* UPWARDS WHITE ARROW -> '|' */
+ { 0xE8, 0x2D }, /* RIGHTWARDS WHITE ARROW -> '-' */
+ { 0xE9, 0x00 }, /* DOWNWARDS WHITE ARROW -> ... */
+ { 0xEF, 0x7C }, /* UPWARDS WHITE DOUBLE ARROW ON PEDESTAL -> '|' */
+ { 0xF0, 0x2D }, /* RIGHTWARDS WHITE ARROW FROM WALL -> '-' */
+ { 0xF1, 0x5C }, /* NORTH WEST ARROW TO CORNER -> '\' */
+ { 0xF2, 0x5C }, /* SOUTH EAST ARROW TO CORNER -> '\' */
+ { 0xF3, 0x7C }, /* UP DOWN WHITE ARROW -> '|' */
+ /* Entries for page 0x22 */
+ { 0x04, 0x21 }, /* THERE DOES NOT EXIST -> '!' */
+ { 0x09, 0x21 }, /* NOT AN ELEMENT OF -> '!' */
+ { 0x0C, 0x21 }, /* DOES NOT CONTAIN AS MEMBER -> '!' */
+ { 0x12, 0x2D }, /* MINUS SIGN -> '-' */
+ { 0x15, 0x2F }, /* DIVISION SLASH -> '/' */
+ { 0x16, 0x5C }, /* SET MINUS -> '\' */
+ { 0x17, 0x2A }, /* ASTERISK OPERATOR -> '*' */
+ { 0x18, 0x6F }, /* RING OPERATOR -> 'o' */
+ { 0x19, 0x2E }, /* BULLET OPERATOR -> '.' */
+ { 0x23, 0x7C }, /* DIVIDES -> '|' */
+ { 0x24, 0x21 }, /* DOES NOT DIVIDE -> '!' */
+ { 0x26, 0x21 }, /* NOT PARALLEL TO -> '!' */
+ { 0x36, 0x3A }, /* RATIO -> ':' */
+ { 0x3C, 0x7E }, /* TILDE OPERATOR -> '~' */
+ { 0x41, 0x23 }, /* NOT TILDE -> '#' */
+ { 0x44, 0x23 }, /* NOT ASYMPTOTICALLY EQUAL TO -> '#' */
+ { 0x49, 0x23 }, /* NOT ALMOST EQUAL TO -> '#' */
+ { 0x60, 0x23 }, /* NOT EQUAL TO -> '#' */
+ { 0x62, 0x23 }, /* NOT IDENTICAL TO -> '#' */
+ { 0x64, 0x3C }, /* LESS-THAN OR EQUAL TO -> '<' */
+ { 0x65, 0x3E }, /* GREATER-THAN OR EQUAL TO -> '>' */
+ { 0x68, 0x23 }, /* LESS-THAN BUT NOT EQUAL TO -> '#' */
+ { 0x69, 0x23 }, /* GREATER-THAN BUT NOT EQUAL TO -> '#' */
+ { 0x6D, 0x23 }, /* NOT EQUIVALENT TO -> '#' */
+ { 0x6E, 0x21 }, /* NOT LESS-THAN -> '!' */
+ { 0x6F, 0x21 }, /* NOT GREATER-THAN -> '!' */
+ { 0x80, 0x21 }, /* DOES NOT PRECEDE -> '!' */
+ { 0x81, 0x21 }, /* DOES NOT SUCCEED -> '!' */
+ { 0x84, 0x21 }, /* NOT A SUBSET OF -> '!' */
+ { 0x85, 0x21 }, /* NOT A SUPERSET OF -> '!' */
+ { 0x8A, 0x23 }, /* SUBSET OF WITH NOT EQUAL TO -> '#' */
+ { 0x8B, 0x23 }, /* SUPERSET OF WITH NOT EQUAL TO -> '#' */
+ { 0x9B, 0x2A }, /* CIRCLED ASTERISK OPERATOR -> '*' */
+ { 0xC6, 0x2A }, /* STAR OPERATOR -> '*' */
+ /* Entries for page 0x23 */
+ { 0x03, 0x5E }, /* UP ARROWHEAD -> '^' */
+ { 0x29, 0x3C }, /* LEFT-POINTING ANGLE BRACKET -> '<' */
+ { 0x5F, 0x2A }, /* APL FUNCTIONAL SYMBOL CIRCLE STAR -> '*' */
+ { 0x63, 0x2A }, /* APL FUNCTIONAL SYMBOL STAR DIAERESIS -> '*' */
+ /* Entries for page 0x24 */
+ { 0x60, 0x31 }, /* CIRCLED DIGIT ONE -> '1' */
+ { 0x61, 0x32 }, /* CIRCLED DIGIT TWO -> '2' */
+ { 0x62, 0x33 }, /* CIRCLED DIGIT THREE -> '3' */
+ { 0x63, 0x34 }, /* CIRCLED DIGIT FOUR -> '4' */
+ { 0x64, 0x35 }, /* CIRCLED DIGIT FIVE -> '5' */
+ { 0x65, 0x36 }, /* CIRCLED DIGIT SIX -> '6' */
+ { 0x66, 0x37 }, /* CIRCLED DIGIT SEVEN -> '7' */
+ { 0x67, 0x38 }, /* CIRCLED DIGIT EIGHT -> '8' */
+ { 0x68, 0x39 }, /* CIRCLED DIGIT NINE -> '9' */
+ { 0xB6, 0x41 }, /* CIRCLED LATIN CAPITAL LETTER A -> 'A' */
+ { 0xB7, 0x42 }, /* CIRCLED LATIN CAPITAL LETTER B -> 'B' */
+ { 0xB8, 0x43 }, /* CIRCLED LATIN CAPITAL LETTER C -> 'C' */
+ { 0xB9, 0x44 }, /* CIRCLED LATIN CAPITAL LETTER D -> 'D' */
+ { 0xBA, 0x45 }, /* CIRCLED LATIN CAPITAL LETTER E -> 'E' */
+ { 0xBB, 0x46 }, /* CIRCLED LATIN CAPITAL LETTER F -> 'F' */
+ { 0xBC, 0x47 }, /* CIRCLED LATIN CAPITAL LETTER G -> 'G' */
+ { 0xBD, 0x48 }, /* CIRCLED LATIN CAPITAL LETTER H -> 'H' */
+ { 0xBE, 0x49 }, /* CIRCLED LATIN CAPITAL LETTER I -> 'I' */
+ { 0xBF, 0x4A }, /* CIRCLED LATIN CAPITAL LETTER J -> 'J' */
+ { 0xC0, 0x4B }, /* CIRCLED LATIN CAPITAL LETTER K -> 'K' */
+ { 0xC1, 0x4C }, /* CIRCLED LATIN CAPITAL LETTER L -> 'L' */
+ { 0xC2, 0x4D }, /* CIRCLED LATIN CAPITAL LETTER M -> 'M' */
+ { 0xC3, 0x4E }, /* CIRCLED LATIN CAPITAL LETTER N -> 'N' */
+ { 0xC4, 0x4F }, /* CIRCLED LATIN CAPITAL LETTER O -> 'O' */
+ { 0xC5, 0x50 }, /* CIRCLED LATIN CAPITAL LETTER P -> 'P' */
+ { 0xC6, 0x51 }, /* CIRCLED LATIN CAPITAL LETTER Q -> 'Q' */
+ { 0xC7, 0x52 }, /* CIRCLED LATIN CAPITAL LETTER R -> 'R' */
+ { 0xC8, 0x53 }, /* CIRCLED LATIN CAPITAL LETTER S -> 'S' */
+ { 0xC9, 0x54 }, /* CIRCLED LATIN CAPITAL LETTER T -> 'T' */
+ { 0xCA, 0x55 }, /* CIRCLED LATIN CAPITAL LETTER U -> 'U' */
+ { 0xCB, 0x56 }, /* CIRCLED LATIN CAPITAL LETTER V -> 'V' */
+ { 0xCC, 0x57 }, /* CIRCLED LATIN CAPITAL LETTER W -> 'W' */
+ { 0xCD, 0x58 }, /* CIRCLED LATIN CAPITAL LETTER X -> 'X' */
+ { 0xCE, 0x59 }, /* CIRCLED LATIN CAPITAL LETTER Y -> 'Y' */
+ { 0xCF, 0x5A }, /* CIRCLED LATIN CAPITAL LETTER Z -> 'Z' */
+ { 0xD0, 0x61 }, /* CIRCLED LATIN SMALL LETTER A -> 'a' */
+ { 0xD1, 0x62 }, /* CIRCLED LATIN SMALL LETTER B -> 'b' */
+ { 0xD2, 0x63 }, /* CIRCLED LATIN SMALL LETTER C -> 'c' */
+ { 0xD3, 0x64 }, /* CIRCLED LATIN SMALL LETTER D -> 'd' */
+ { 0xD4, 0x65 }, /* CIRCLED LATIN SMALL LETTER E -> 'e' */
+ { 0xD5, 0x66 }, /* CIRCLED LATIN SMALL LETTER F -> 'f' */
+ { 0xD6, 0x67 }, /* CIRCLED LATIN SMALL LETTER G -> 'g' */
+ { 0xD7, 0x68 }, /* CIRCLED LATIN SMALL LETTER H -> 'h' */
+ { 0xD8, 0x69 }, /* CIRCLED LATIN SMALL LETTER I -> 'i' */
+ { 0xD9, 0x6A }, /* CIRCLED LATIN SMALL LETTER J -> 'j' */
+ { 0xDA, 0x6B }, /* CIRCLED LATIN SMALL LETTER K -> 'k' */
+ { 0xDB, 0x6C }, /* CIRCLED LATIN SMALL LETTER L -> 'l' */
+ { 0xDC, 0x6D }, /* CIRCLED LATIN SMALL LETTER M -> 'm' */
+ { 0xDD, 0x6E }, /* CIRCLED LATIN SMALL LETTER N -> 'n' */
+ { 0xDE, 0x6F }, /* CIRCLED LATIN SMALL LETTER O -> 'o' */
+ { 0xDF, 0x70 }, /* CIRCLED LATIN SMALL LETTER P -> 'p' */
+ { 0xE0, 0x71 }, /* CIRCLED LATIN SMALL LETTER Q -> 'q' */
+ { 0xE1, 0x72 }, /* CIRCLED LATIN SMALL LETTER R -> 'r' */
+ { 0xE2, 0x73 }, /* CIRCLED LATIN SMALL LETTER S -> 's' */
+ { 0xE3, 0x74 }, /* CIRCLED LATIN SMALL LETTER T -> 't' */
+ { 0xE4, 0x75 }, /* CIRCLED LATIN SMALL LETTER U -> 'u' */
+ { 0xE5, 0x76 }, /* CIRCLED LATIN SMALL LETTER V -> 'v' */
+ { 0xE6, 0x77 }, /* CIRCLED LATIN SMALL LETTER W -> 'w' */
+ { 0xE7, 0x78 }, /* CIRCLED LATIN SMALL LETTER X -> 'x' */
+ { 0xE8, 0x79 }, /* CIRCLED LATIN SMALL LETTER Y -> 'y' */
+ { 0xE9, 0x7A }, /* CIRCLED LATIN SMALL LETTER Z -> 'z' */
+ { 0xEA, 0x30 }, /* CIRCLED DIGIT ZERO -> '0' */
+ { 0xF5, 0x31 }, /* DOUBLE CIRCLED DIGIT ONE -> '1' */
+ { 0xF6, 0x32 }, /* DOUBLE CIRCLED DIGIT TWO -> '2' */
+ { 0xF7, 0x33 }, /* DOUBLE CIRCLED DIGIT THREE -> '3' */
+ { 0xF8, 0x34 }, /* DOUBLE CIRCLED DIGIT FOUR -> '4' */
+ { 0xF9, 0x35 }, /* DOUBLE CIRCLED DIGIT FIVE -> '5' */
+ { 0xFA, 0x36 }, /* DOUBLE CIRCLED DIGIT SIX -> '6' */
+ { 0xFB, 0x37 }, /* DOUBLE CIRCLED DIGIT SEVEN -> '7' */
+ { 0xFC, 0x38 }, /* DOUBLE CIRCLED DIGIT EIGHT -> '8' */
+ { 0xFD, 0x39 }, /* DOUBLE CIRCLED DIGIT NINE -> '9' */
+ { 0xFF, 0x30 }, /* NEGATIVE CIRCLED DIGIT ZERO -> '0' */
+ /* Entries for page 0x25 */
+ { 0x00, 0x2D }, /* BOX DRAWINGS LIGHT HORIZONTAL -> '-' */
+ { 0x01, 0x2D }, /* BOX DRAWINGS HEAVY HORIZONTAL -> '-' */
+ { 0x02, 0x7C }, /* BOX DRAWINGS LIGHT VERTICAL -> '|' */
+ { 0x03, 0x7C }, /* BOX DRAWINGS HEAVY VERTICAL -> '|' */
+ { 0x04, 0x2D }, /* BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL -> '-' */
+ { 0x05, 0x2D }, /* BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL -> '-' */
+ { 0x06, 0x7C }, /* BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL -> '|' */
+ { 0x07, 0x7C }, /* BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL -> '|' */
+ { 0x08, 0x2D }, /* BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL -> '-' */
+ { 0x09, 0x2D }, /* BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL -> '-' */
+ { 0x0A, 0x7C }, /* BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL -> '|' */
+ { 0x0B, 0x7C }, /* BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL -> '|' */
+ { 0x0C, 0x00 }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT -> ... */
+ { 0x4B, 0x2B }, /* BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL -> '+' */
+ { 0x4C, 0x2D }, /* BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL -> '-' */
+ { 0x4D, 0x2D }, /* BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL -> '-' */
+ { 0x4E, 0x7C }, /* BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL -> '|' */
+ { 0x4F, 0x7C }, /* BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL -> '|' */
+ { 0x50, 0x2D }, /* BOX DRAWINGS DOUBLE HORIZONTAL -> '-' */
+ { 0x51, 0x7C }, /* BOX DRAWINGS DOUBLE VERTICAL -> '|' */
+ { 0x52, 0x00 }, /* BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE -> ... */
+ { 0x70, 0x2B }, /* BOX DRAWINGS LIGHT ARC UP AND RIGHT -> '+' */
+ { 0x71, 0x2F }, /* BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT -> '/' */
+ { 0x72, 0x5C }, /* BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT -> '\' */
+ { 0x73, 0x58 }, /* BOX DRAWINGS LIGHT DIAGONAL CROSS -> 'X' */
+ { 0x74, 0x2D }, /* BOX DRAWINGS LIGHT LEFT -> '-' */
+ { 0x75, 0x7C }, /* BOX DRAWINGS LIGHT UP -> '|' */
+ { 0x76, 0x2D }, /* BOX DRAWINGS LIGHT RIGHT -> '-' */
+ { 0x77, 0x7C }, /* BOX DRAWINGS LIGHT DOWN -> '|' */
+ { 0x78, 0x2D }, /* BOX DRAWINGS HEAVY LEFT -> '-' */
+ { 0x79, 0x7C }, /* BOX DRAWINGS HEAVY UP -> '|' */
+ { 0x7A, 0x2D }, /* BOX DRAWINGS HEAVY RIGHT -> '-' */
+ { 0x7B, 0x7C }, /* BOX DRAWINGS HEAVY DOWN -> '|' */
+ { 0x7C, 0x2D }, /* BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT -> '-' */
+ { 0x7D, 0x7C }, /* BOX DRAWINGS LIGHT UP AND HEAVY DOWN -> '|' */
+ { 0x7E, 0x2D }, /* BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT -> '-' */
+ { 0x7F, 0x7C }, /* BOX DRAWINGS HEAVY UP AND LIGHT DOWN -> '|' */
+ { 0x80, 0x00 }, /* UPPER HALF BLOCK -> ... */
+ { 0x93, 0x23 }, /* DARK SHADE -> '#' */
+ { 0x94, 0x2D }, /* UPPER ONE EIGHTH BLOCK -> '-' */
+ { 0x95, 0x7C }, /* RIGHT ONE EIGHTH BLOCK -> '|' */
+ { 0x96, 0x00 }, /* QUADRANT LOWER LEFT -> ... */
+ { 0xB1, 0x23 }, /* WHITE PARALLELOGRAM -> '#' */
+ { 0xB2, 0x00 }, /* BLACK UP-POINTING TRIANGLE -> ... */
+ { 0xB5, 0x5E }, /* WHITE UP-POINTING SMALL TRIANGLE -> '^' */
+ { 0xB6, 0x00 }, /* BLACK RIGHT-POINTING TRIANGLE -> ... */
+ { 0xBB, 0x3E }, /* WHITE RIGHT-POINTING POINTER -> '>' */
+ { 0xBC, 0x00 }, /* BLACK DOWN-POINTING TRIANGLE -> ... */
+ { 0xBF, 0x56 }, /* WHITE DOWN-POINTING SMALL TRIANGLE -> 'V' */
+ { 0xC0, 0x00 }, /* BLACK LEFT-POINTING TRIANGLE -> ... */
+ { 0xC5, 0x3C }, /* WHITE LEFT-POINTING POINTER -> '<' */
+ { 0xC6, 0x00 }, /* BLACK DIAMOND -> ... */
+ { 0xE6, 0x2A }, /* WHITE BULLET -> '*' */
+ { 0xE7, 0x00 }, /* SQUARE WITH LEFT HALF BLACK -> ... */
+ { 0xEB, 0x23 }, /* WHITE SQUARE WITH VERTICAL BISECTING LINE -> '#' */
+ { 0xEC, 0x00 }, /* WHITE UP-POINTING TRIANGLE WITH DOT -> ... */
+ { 0xEE, 0x5E }, /* UP-POINTING TRIANGLE WITH RIGHT HALF BLACK -> '^' */
+ { 0xEF, 0x4F }, /* LARGE CIRCLE -> 'O' */
+ { 0xF0, 0x00 }, /* WHITE SQUARE WITH UPPER LEFT QUADRANT -> ... */
+ { 0xF7, 0x23 }, /* WHITE CIRCLE WITH UPPER RIGHT QUADRANT -> '#' */
+ /* Entries for page 0x26 */
+ { 0x05, 0x2A }, /* BLACK STAR -> '*' */
+ { 0x06, 0x2A }, /* WHITE STAR -> '*' */
+ { 0x2A, 0x2A }, /* STAR AND CRESCENT -> '*' */
+ { 0x6F, 0x23 }, /* MUSIC SHARP SIGN -> '#' */
+ { 0x98, 0x2A }, /* FLOWER -> '*' */
+ { 0x9D, 0x2A }, /* OUTLINED WHITE STAR -> '*' */
+ /* Entries for page 0x27 */
+ { 0x13, 0x76 }, /* CHECK MARK -> 'v' */
+ { 0x14, 0x56 }, /* HEAVY CHECK MARK -> 'V' */
+ { 0x15, 0x78 }, /* MULTIPLICATION X -> 'x' */
+ { 0x16, 0x58 }, /* HEAVY MULTIPLICATION X -> 'X' */
+ { 0x17, 0x78 }, /* BALLOT X -> 'x' */
+ { 0x18, 0x58 }, /* HEAVY BALLOT X -> 'X' */
+ { 0x21, 0x00 }, /* STAR OF DAVID -> ... */
+ { 0x46, 0x2A }, /* HEAVY CHEVRON SNOWFLAKE -> '*' */
+ { 0x49, 0x00 }, /* BALLOON-SPOKED ASTERISK -> ... */
+ { 0x4B, 0x2A }, /* HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK -> '*' */
+ { 0x58, 0x7C }, /* LIGHT VERTICAL BAR -> '|' */
+ { 0x5C, 0x27 }, /* HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT -> ''' */
+ { 0x5D, 0x22 }, /* HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT -> '"' */
+ { 0x5E, 0x22 }, /* HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT -> '"' */
+ { 0x5F, 0x2C }, /* HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT -> ',' */
+ { 0x62, 0x21 }, /* HEAVY EXCLAMATION MARK ORNAMENT -> '!' */
+ { 0xE6, 0x5B }, /* MATHEMATICAL LEFT WHITE SQUARE BRACKET -> '[' */
+ { 0xE8, 0x3C }, /* MATHEMATICAL LEFT ANGLE BRACKET -> '<' */
+ /* Entries for page 0x28 */
+ { 0x00, 0x20 }, /* BRAILLE PATTERN BLANK -> ' ' */
+ { 0x01, 0x61 }, /* BRAILLE PATTERN DOTS-1 -> 'a' */
+ { 0x02, 0x31 }, /* BRAILLE PATTERN DOTS-2 -> '1' */
+ { 0x03, 0x62 }, /* BRAILLE PATTERN DOTS-12 -> 'b' */
+ { 0x04, 0x27 }, /* BRAILLE PATTERN DOTS-3 -> ''' */
+ { 0x05, 0x6B }, /* BRAILLE PATTERN DOTS-13 -> 'k' */
+ { 0x06, 0x32 }, /* BRAILLE PATTERN DOTS-23 -> '2' */
+ { 0x07, 0x6C }, /* BRAILLE PATTERN DOTS-123 -> 'l' */
+ { 0x08, 0x40 }, /* BRAILLE PATTERN DOTS-4 -> '@' */
+ { 0x09, 0x63 }, /* BRAILLE PATTERN DOTS-14 -> 'c' */
+ { 0x0A, 0x69 }, /* BRAILLE PATTERN DOTS-24 -> 'i' */
+ { 0x0B, 0x66 }, /* BRAILLE PATTERN DOTS-124 -> 'f' */
+ { 0x0C, 0x2F }, /* BRAILLE PATTERN DOTS-34 -> '/' */
+ { 0x0D, 0x6D }, /* BRAILLE PATTERN DOTS-134 -> 'm' */
+ { 0x0E, 0x73 }, /* BRAILLE PATTERN DOTS-234 -> 's' */
+ { 0x0F, 0x70 }, /* BRAILLE PATTERN DOTS-1234 -> 'p' */
+ { 0x10, 0x22 }, /* BRAILLE PATTERN DOTS-5 -> '"' */
+ { 0x11, 0x65 }, /* BRAILLE PATTERN DOTS-15 -> 'e' */
+ { 0x12, 0x33 }, /* BRAILLE PATTERN DOTS-25 -> '3' */
+ { 0x13, 0x68 }, /* BRAILLE PATTERN DOTS-125 -> 'h' */
+ { 0x14, 0x39 }, /* BRAILLE PATTERN DOTS-35 -> '9' */
+ { 0x15, 0x6F }, /* BRAILLE PATTERN DOTS-135 -> 'o' */
+ { 0x16, 0x36 }, /* BRAILLE PATTERN DOTS-235 -> '6' */
+ { 0x17, 0x72 }, /* BRAILLE PATTERN DOTS-1235 -> 'r' */
+ { 0x18, 0x5E }, /* BRAILLE PATTERN DOTS-45 -> '^' */
+ { 0x19, 0x64 }, /* BRAILLE PATTERN DOTS-145 -> 'd' */
+ { 0x1A, 0x6A }, /* BRAILLE PATTERN DOTS-245 -> 'j' */
+ { 0x1B, 0x67 }, /* BRAILLE PATTERN DOTS-1245 -> 'g' */
+ { 0x1C, 0x3E }, /* BRAILLE PATTERN DOTS-345 -> '>' */
+ { 0x1D, 0x6E }, /* BRAILLE PATTERN DOTS-1345 -> 'n' */
+ { 0x1E, 0x74 }, /* BRAILLE PATTERN DOTS-2345 -> 't' */
+ { 0x1F, 0x71 }, /* BRAILLE PATTERN DOTS-12345 -> 'q' */
+ { 0x20, 0x2C }, /* BRAILLE PATTERN DOTS-6 -> ',' */
+ { 0x21, 0x2A }, /* BRAILLE PATTERN DOTS-16 -> '*' */
+ { 0x22, 0x35 }, /* BRAILLE PATTERN DOTS-26 -> '5' */
+ { 0x23, 0x3C }, /* BRAILLE PATTERN DOTS-126 -> '<' */
+ { 0x24, 0x2D }, /* BRAILLE PATTERN DOTS-36 -> '-' */
+ { 0x25, 0x75 }, /* BRAILLE PATTERN DOTS-136 -> 'u' */
+ { 0x26, 0x38 }, /* BRAILLE PATTERN DOTS-236 -> '8' */
+ { 0x27, 0x76 }, /* BRAILLE PATTERN DOTS-1236 -> 'v' */
+ { 0x28, 0x2E }, /* BRAILLE PATTERN DOTS-46 -> '.' */
+ { 0x29, 0x25 }, /* BRAILLE PATTERN DOTS-146 -> '%' */
+ { 0x2A, 0x5B }, /* BRAILLE PATTERN DOTS-246 -> '[' */
+ { 0x2B, 0x24 }, /* BRAILLE PATTERN DOTS-1246 -> '$' */
+ { 0x2C, 0x2B }, /* BRAILLE PATTERN DOTS-346 -> '+' */
+ { 0x2D, 0x78 }, /* BRAILLE PATTERN DOTS-1346 -> 'x' */
+ { 0x2E, 0x21 }, /* BRAILLE PATTERN DOTS-2346 -> '!' */
+ { 0x2F, 0x26 }, /* BRAILLE PATTERN DOTS-12346 -> '&' */
+ { 0x30, 0x3B }, /* BRAILLE PATTERN DOTS-56 -> ';' */
+ { 0x31, 0x3A }, /* BRAILLE PATTERN DOTS-156 -> ':' */
+ { 0x32, 0x34 }, /* BRAILLE PATTERN DOTS-256 -> '4' */
+ { 0x33, 0x5C }, /* BRAILLE PATTERN DOTS-1256 -> '\' */
+ { 0x34, 0x30 }, /* BRAILLE PATTERN DOTS-356 -> '0' */
+ { 0x35, 0x7A }, /* BRAILLE PATTERN DOTS-1356 -> 'z' */
+ { 0x36, 0x37 }, /* BRAILLE PATTERN DOTS-2356 -> '7' */
+ { 0x37, 0x28 }, /* BRAILLE PATTERN DOTS-12356 -> '(' */
+ { 0x38, 0x5F }, /* BRAILLE PATTERN DOTS-456 -> '_' */
+ { 0x39, 0x3F }, /* BRAILLE PATTERN DOTS-1456 -> '?' */
+ { 0x3A, 0x77 }, /* BRAILLE PATTERN DOTS-2456 -> 'w' */
+ { 0x3B, 0x5D }, /* BRAILLE PATTERN DOTS-12456 -> ']' */
+ { 0x3C, 0x23 }, /* BRAILLE PATTERN DOTS-3456 -> '#' */
+ { 0x3D, 0x79 }, /* BRAILLE PATTERN DOTS-13456 -> 'y' */
+ { 0x3E, 0x29 }, /* BRAILLE PATTERN DOTS-23456 -> ')' */
+ { 0x3F, 0x3D }, /* BRAILLE PATTERN DOTS-123456 -> '=' */
+ /* Entries for page 0x29 */
+ { 0x83, 0x7B }, /* LEFT WHITE CURLY BRACKET -> '{' */
+ /* Entries for page 0x2C */
+ { 0x60, 0x4C }, /* LATIN CAPITAL LETTER L WITH DOUBLE BAR -> 'L' */
+ { 0x61, 0x6C }, /* LATIN SMALL LETTER L WITH DOUBLE BAR -> 'l' */
+ { 0x62, 0x4C }, /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE -> 'L' */
+ { 0x63, 0x50 }, /* LATIN CAPITAL LETTER P WITH STROKE -> 'P' */
+ { 0x64, 0x52 }, /* LATIN CAPITAL LETTER R WITH TAIL -> 'R' */
+ { 0x65, 0x61 }, /* LATIN SMALL LETTER A WITH STROKE -> 'a' */
+ { 0x66, 0x74 }, /* LATIN SMALL LETTER T WITH DIAGONAL STROKE -> 't' */
+ { 0x67, 0x48 }, /* LATIN CAPITAL LETTER H WITH DESCENDER -> 'H' */
+ { 0x68, 0x68 }, /* LATIN SMALL LETTER H WITH DESCENDER -> 'h' */
+ { 0x69, 0x4B }, /* LATIN CAPITAL LETTER K WITH DESCENDER -> 'K' */
+ { 0x6A, 0x6B }, /* LATIN SMALL LETTER K WITH DESCENDER -> 'k' */
+ { 0x6B, 0x5A }, /* LATIN CAPITAL LETTER Z WITH DESCENDER -> 'Z' */
+ { 0x6C, 0x7A }, /* LATIN SMALL LETTER Z WITH DESCENDER -> 'z' */
+ { 0x6E, 0x4D }, /* LATIN CAPITAL LETTER M WITH HOOK -> 'M' */
+ { 0x6F, 0x41 }, /* LATIN CAPITAL LETTER TURNED A -> 'A' */
+ /* Entries for page 0x2E */
+ { 0x00, 0x72 }, /* RIGHT ANGLE SUBSTITUTION MARKER -> 'r' */
+ { 0x06, 0x54 }, /* RAISED INTERPOLATION MARKER -> 'T' */
+ { 0x09, 0x73 }, /* LEFT TRANSPOSITION BRACKET -> 's' */
+ { 0x0C, 0x5C }, /* LEFT RAISED OMISSION BRACKET -> '\' */
+ { 0x0D, 0x2F }, /* RIGHT RAISED OMISSION BRACKET -> '/' */
+ { 0x12, 0x3E }, /* HYPODIASTOLE -> '>' */
+ { 0x13, 0x25 }, /* DOTTED OBELOS -> '%' */
+ { 0x16, 0x3E }, /* DOTTED RIGHT-POINTING ANGLE -> '>' */
+ { 0x17, 0x3D }, /* DOUBLE OBLIQUE HYPHEN -> '=' */
+ { 0x19, 0x2F }, /* PALM BRANCH -> '/' */
+ { 0x1A, 0x2D }, /* HYPHEN WITH DIAERESIS -> '-' */
+ { 0x1B, 0x7E }, /* TILDE WITH RING ABOVE -> '~' */
+ { 0x1C, 0x5C }, /* LEFT LOW PARAPHRASE BRACKET -> '\' */
+ { 0x1D, 0x2F }, /* RIGHT LOW PARAPHRASE BRACKET -> '/' */
+ { 0x1E, 0x7E }, /* TILDE WITH DOT ABOVE -> '~' */
+ { 0x1F, 0x7E }, /* TILDE WITH DOT BELOW -> '~' */
+ { 0x2E, 0x3F }, /* REVERSED QUESTION MARK -> '?' */
+ { 0x2F, 0x27 }, /* VERTICAL TILDE -> ''' */
+ { 0x30, 0x6F }, /* RING POINT -> 'o' */
+ { 0x31, 0x2E }, /* WORD SEPARATOR MIDDLE DOT -> '.' */
+ { 0x32, 0x2C }, /* TURNED COMMA -> ',' */
+ { 0x33, 0x2E }, /* RAISED DOT -> '.' */
+ { 0x34, 0x2C }, /* RAISED COMMA -> ',' */
+ { 0x35, 0x3B }, /* TURNED SEMICOLON -> ';' */
+ { 0x3C, 0x78 }, /* STENOGRAPHIC FULL STOP -> 'x' */
+ { 0x3D, 0x7C }, /* VERTICAL SIX DOTS -> '|' */
+ { 0x40, 0x3D }, /* DOUBLE HYPHEN -> '=' */
+ { 0x41, 0x2C }, /* REVERSED COMMA -> ',' */
+ { 0x42, 0x22 }, /* DOUBLE LOW-REVERSED-9 QUOTATION MARK -> '"' */
+ /* Entries for page 0x30 */
+ { 0x00, 0x20 }, /* IDEOGRAPHIC SPACE -> ' ' */
+ { 0x03, 0x22 }, /* DITTO MARK -> '"' */
+ { 0x05, 0x22 }, /* IDEOGRAPHIC ITERATION MARK -> '"' */
+ { 0x06, 0x2F }, /* IDEOGRAPHIC CLOSING MARK -> '/' */
+ { 0x07, 0x30 }, /* IDEOGRAPHIC NUMBER ZERO -> '0' */
+ { 0x08, 0x3C }, /* LEFT ANGLE BRACKET -> '<' */
+ { 0x0C, 0x5B }, /* LEFT CORNER BRACKET -> '[' */
+ { 0x0E, 0x7B }, /* LEFT WHITE CORNER BRACKET -> '{' */
+ { 0x12, 0x40 }, /* POSTAL MARK -> '@' */
+ { 0x14, 0x5B }, /* LEFT TORTOISE SHELL BRACKET -> '[' */
+ { 0x20, 0x40 }, /* POSTAL MARK FACE -> '@' */
+ { 0x21, 0x31 }, /* HANGZHOU NUMERAL ONE -> '1' */
+ { 0x22, 0x32 }, /* HANGZHOU NUMERAL TWO -> '2' */
+ { 0x23, 0x33 }, /* HANGZHOU NUMERAL THREE -> '3' */
+ { 0x24, 0x34 }, /* HANGZHOU NUMERAL FOUR -> '4' */
+ { 0x25, 0x35 }, /* HANGZHOU NUMERAL FIVE -> '5' */
+ { 0x26, 0x36 }, /* HANGZHOU NUMERAL SIX -> '6' */
+ { 0x27, 0x37 }, /* HANGZHOU NUMERAL SEVEN -> '7' */
+ { 0x28, 0x38 }, /* HANGZHOU NUMERAL EIGHT -> '8' */
+ { 0x29, 0x39 }, /* HANGZHOU NUMERAL NINE -> '9' */
+ { 0x30, 0x7E }, /* WAVY DASH -> '~' */
+ { 0x31, 0x00 }, /* VERTICAL KANA REPEAT MARK -> ... */
+ { 0x34, 0x2B }, /* VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF -> '+' */
+ { 0x36, 0x40 }, /* CIRCLED POSTAL MARK -> '@' */
+ { 0x41, 0x61 }, /* HIRAGANA LETTER SMALL A -> 'a' */
+ { 0x42, 0x61 }, /* HIRAGANA LETTER A -> 'a' */
+ { 0x43, 0x69 }, /* HIRAGANA LETTER SMALL I -> 'i' */
+ { 0x44, 0x69 }, /* HIRAGANA LETTER I -> 'i' */
+ { 0x45, 0x75 }, /* HIRAGANA LETTER SMALL U -> 'u' */
+ { 0x46, 0x75 }, /* HIRAGANA LETTER U -> 'u' */
+ { 0x47, 0x65 }, /* HIRAGANA LETTER SMALL E -> 'e' */
+ { 0x48, 0x65 }, /* HIRAGANA LETTER E -> 'e' */
+ { 0x49, 0x6F }, /* HIRAGANA LETTER SMALL O -> 'o' */
+ { 0x4A, 0x6F }, /* HIRAGANA LETTER O -> 'o' */
+ { 0x93, 0x6E }, /* HIRAGANA LETTER N -> 'n' */
+ { 0x9D, 0x22 }, /* HIRAGANA ITERATION MARK -> '"' */
+ { 0x9E, 0x22 }, /* HIRAGANA VOICED ITERATION MARK -> '"' */
+ { 0xA0, 0x3D }, /* KATAKANA-HIRAGANA DOUBLE HYPHEN -> '=' */
+ { 0xA1, 0x61 }, /* KATAKANA LETTER SMALL A -> 'a' */
+ { 0xA2, 0x61 }, /* KATAKANA LETTER A -> 'a' */
+ { 0xA3, 0x69 }, /* KATAKANA LETTER SMALL I -> 'i' */
+ { 0xA4, 0x69 }, /* KATAKANA LETTER I -> 'i' */
+ { 0xA5, 0x75 }, /* KATAKANA LETTER SMALL U -> 'u' */
+ { 0xA6, 0x75 }, /* KATAKANA LETTER U -> 'u' */
+ { 0xA7, 0x65 }, /* KATAKANA LETTER SMALL E -> 'e' */
+ { 0xA8, 0x65 }, /* KATAKANA LETTER E -> 'e' */
+ { 0xA9, 0x6F }, /* KATAKANA LETTER SMALL O -> 'o' */
+ { 0xAA, 0x6F }, /* KATAKANA LETTER O -> 'o' */
+ { 0xF3, 0x6E }, /* KATAKANA LETTER N -> 'n' */
+ { 0xFB, 0x2A }, /* KATAKANA MIDDLE DOT -> '*' */
+ { 0xFC, 0x2D }, /* KATAKANA-HIRAGANA PROLONGED SOUND MARK -> '-' */
+ { 0xFD, 0x22 }, /* KATAKANA ITERATION MARK -> '"' */
+ { 0xFE, 0x22 }, /* KATAKANA VOICED ITERATION MARK -> '"' */
+ /* Entries for page 0x31 */
+ { 0x05, 0x42 }, /* BOPOMOFO LETTER B -> 'B' */
+ { 0x06, 0x50 }, /* BOPOMOFO LETTER P -> 'P' */
+ { 0x07, 0x4D }, /* BOPOMOFO LETTER M -> 'M' */
+ { 0x08, 0x46 }, /* BOPOMOFO LETTER F -> 'F' */
+ { 0x09, 0x44 }, /* BOPOMOFO LETTER D -> 'D' */
+ { 0x0A, 0x54 }, /* BOPOMOFO LETTER T -> 'T' */
+ { 0x0B, 0x4E }, /* BOPOMOFO LETTER N -> 'N' */
+ { 0x0C, 0x4C }, /* BOPOMOFO LETTER L -> 'L' */
+ { 0x0D, 0x47 }, /* BOPOMOFO LETTER G -> 'G' */
+ { 0x0E, 0x4B }, /* BOPOMOFO LETTER K -> 'K' */
+ { 0x0F, 0x48 }, /* BOPOMOFO LETTER H -> 'H' */
+ { 0x10, 0x4A }, /* BOPOMOFO LETTER J -> 'J' */
+ { 0x11, 0x51 }, /* BOPOMOFO LETTER Q -> 'Q' */
+ { 0x12, 0x58 }, /* BOPOMOFO LETTER X -> 'X' */
+ { 0x16, 0x52 }, /* BOPOMOFO LETTER R -> 'R' */
+ { 0x17, 0x5A }, /* BOPOMOFO LETTER Z -> 'Z' */
+ { 0x18, 0x43 }, /* BOPOMOFO LETTER C -> 'C' */
+ { 0x19, 0x53 }, /* BOPOMOFO LETTER S -> 'S' */
+ { 0x1A, 0x41 }, /* BOPOMOFO LETTER A -> 'A' */
+ { 0x1B, 0x4F }, /* BOPOMOFO LETTER O -> 'O' */
+ { 0x1C, 0x45 }, /* BOPOMOFO LETTER E -> 'E' */
+ { 0x27, 0x49 }, /* BOPOMOFO LETTER I -> 'I' */
+ { 0x28, 0x55 }, /* BOPOMOFO LETTER U -> 'U' */
+ { 0x2A, 0x56 }, /* BOPOMOFO LETTER V -> 'V' */
+ { 0x31, 0x67 }, /* HANGUL LETTER KIYEOK -> 'g' */
+ { 0x34, 0x6E }, /* HANGUL LETTER NIEUN -> 'n' */
+ { 0x37, 0x64 }, /* HANGUL LETTER TIKEUT -> 'd' */
+ { 0x39, 0x72 }, /* HANGUL LETTER RIEUL -> 'r' */
+ { 0x41, 0x6D }, /* HANGUL LETTER MIEUM -> 'm' */
+ { 0x42, 0x62 }, /* HANGUL LETTER PIEUP -> 'b' */
+ { 0x45, 0x73 }, /* HANGUL LETTER SIOS -> 's' */
+ { 0x48, 0x6A }, /* HANGUL LETTER CIEUC -> 'j' */
+ { 0x4A, 0x63 }, /* HANGUL LETTER CHIEUCH -> 'c' */
+ { 0x4B, 0x6B }, /* HANGUL LETTER KHIEUKH -> 'k' */
+ { 0x4C, 0x74 }, /* HANGUL LETTER THIEUTH -> 't' */
+ { 0x4D, 0x70 }, /* HANGUL LETTER PHIEUPH -> 'p' */
+ { 0x4E, 0x68 }, /* HANGUL LETTER HIEUH -> 'h' */
+ { 0x4F, 0x61 }, /* HANGUL LETTER A -> 'a' */
+ { 0x54, 0x65 }, /* HANGUL LETTER E -> 'e' */
+ { 0x57, 0x6F }, /* HANGUL LETTER O -> 'o' */
+ { 0x5C, 0x75 }, /* HANGUL LETTER U -> 'u' */
+ { 0x63, 0x69 }, /* HANGUL LETTER I -> 'i' */
+ { 0x7F, 0x5A }, /* HANGUL LETTER PANSIOS -> 'Z' */
+ { 0x81, 0x4E }, /* HANGUL LETTER YESIEUNG -> 'N' */
+ { 0x86, 0x51 }, /* HANGUL LETTER YEORINHIEUH -> 'Q' */
+ { 0x8D, 0x55 }, /* HANGUL LETTER ARAEA -> 'U' */
+ { 0xB4, 0x50 }, /* BOPOMOFO FINAL LETTER P -> 'P' */
+ { 0xB5, 0x54 }, /* BOPOMOFO FINAL LETTER T -> 'T' */
+ { 0xB6, 0x4B }, /* BOPOMOFO FINAL LETTER K -> 'K' */
+ { 0xB7, 0x48 }, /* BOPOMOFO FINAL LETTER H -> 'H' */
+ /* Entries for page 0x32 */
+ { 0xD0, 0x61 }, /* CIRCLED KATAKANA A -> 'a' */
+ { 0xD1, 0x69 }, /* CIRCLED KATAKANA I -> 'i' */
+ { 0xD2, 0x75 }, /* CIRCLED KATAKANA U -> 'u' */
+ { 0xD3, 0x75 }, /* CIRCLED KATAKANA E -> 'u' */
+ { 0xD4, 0x6F }, /* CIRCLED KATAKANA O -> 'o' */
+ /* Entries for page 0xA0 */
+ { 0x02, 0x69 }, /* YI SYLLABLE I -> 'i' */
+ { 0x0A, 0x61 }, /* YI SYLLABLE A -> 'a' */
+ { 0x11, 0x6F }, /* YI SYLLABLE O -> 'o' */
+ { 0x14, 0x65 }, /* YI SYLLABLE E -> 'e' */
+ /* Entries for page 0xC5 */
+ { 0x44, 0x61 }, /* HANGUL SYLLABLE A -> 'a' */
+ { 0xD0, 0x65 }, /* HANGUL SYLLABLE E -> 'e' */
+ /* Entries for page 0xC6 */
+ { 0x24, 0x6F }, /* HANGUL SYLLABLE O -> 'o' */
+ { 0xB0, 0x75 }, /* HANGUL SYLLABLE U -> 'u' */
+ /* Entries for page 0xC7 */
+ { 0x74, 0x69 }, /* HANGUL SYLLABLE I -> 'i' */
+ /* Entries for page 0xFB */
+ { 0x1D, 0x69 }, /* HEBREW LETTER YOD WITH HIRIQ -> 'i' */
+ { 0x20, 0x60 }, /* HEBREW LETTER ALTERNATIVE AYIN -> '`' */
+ { 0x21, 0x41 }, /* HEBREW LETTER WIDE ALEF -> 'A' */
+ { 0x22, 0x64 }, /* HEBREW LETTER WIDE DALET -> 'd' */
+ { 0x23, 0x68 }, /* HEBREW LETTER WIDE HE -> 'h' */
+ { 0x25, 0x6C }, /* HEBREW LETTER WIDE LAMED -> 'l' */
+ { 0x26, 0x6D }, /* HEBREW LETTER WIDE FINAL MEM -> 'm' */
+ { 0x27, 0x72 }, /* HEBREW LETTER WIDE RESH -> 'r' */
+ { 0x28, 0x74 }, /* HEBREW LETTER WIDE TAV -> 't' */
+ { 0x29, 0x2B }, /* HEBREW LETTER ALTERNATIVE PLUS SIGN -> '+' */
+ { 0x2B, 0x53 }, /* HEBREW LETTER SHIN WITH SIN DOT -> 'S' */
+ { 0x2D, 0x53 }, /* HEBREW LETTER SHIN WITH DAGESH AND SIN DOT -> 'S' */
+ { 0x2E, 0x61 }, /* HEBREW LETTER ALEF WITH PATAH -> 'a' */
+ { 0x2F, 0x61 }, /* HEBREW LETTER ALEF WITH QAMATS -> 'a' */
+ { 0x30, 0x41 }, /* HEBREW LETTER ALEF WITH MAPIQ -> 'A' */
+ { 0x31, 0x62 }, /* HEBREW LETTER BET WITH DAGESH -> 'b' */
+ { 0x32, 0x67 }, /* HEBREW LETTER GIMEL WITH DAGESH -> 'g' */
+ { 0x33, 0x64 }, /* HEBREW LETTER DALET WITH DAGESH -> 'd' */
+ { 0x34, 0x68 }, /* HEBREW LETTER HE WITH MAPIQ -> 'h' */
+ { 0x35, 0x76 }, /* HEBREW LETTER VAV WITH DAGESH -> 'v' */
+ { 0x36, 0x7A }, /* HEBREW LETTER ZAYIN WITH DAGESH -> 'z' */
+ { 0x38, 0x74 }, /* HEBREW LETTER TET WITH DAGESH -> 't' */
+ { 0x39, 0x79 }, /* HEBREW LETTER YOD WITH DAGESH -> 'y' */
+ { 0x3C, 0x6C }, /* HEBREW LETTER LAMED WITH DAGESH -> 'l' */
+ { 0x3E, 0x6D }, /* HEBREW LETTER MEM WITH DAGESH -> 'm' */
+ { 0x40, 0x6E }, /* HEBREW LETTER NUN WITH DAGESH -> 'n' */
+ { 0x41, 0x73 }, /* HEBREW LETTER SAMEKH WITH DAGESH -> 's' */
+ { 0x43, 0x70 }, /* HEBREW LETTER FINAL PE WITH DAGESH -> 'p' */
+ { 0x44, 0x70 }, /* HEBREW LETTER PE WITH DAGESH -> 'p' */
+ { 0x47, 0x6B }, /* HEBREW LETTER QOF WITH DAGESH -> 'k' */
+ { 0x48, 0x72 }, /* HEBREW LETTER RESH WITH DAGESH -> 'r' */
+ { 0x4A, 0x74 }, /* HEBREW LETTER TAV WITH DAGESH -> 't' */
+ { 0x4B, 0x6F }, /* HEBREW LETTER VAV WITH HOLAM -> 'o' */
+ { 0x4C, 0x76 }, /* HEBREW LETTER BET WITH RAFE -> 'v' */
+ { 0x4E, 0x66 }, /* HEBREW LETTER PE WITH RAFE -> 'f' */
+ /* Entries for page 0xFE */
+ { 0x23, 0x7E }, /* COMBINING DOUBLE TILDE RIGHT HALF -> '~' */
+ { 0x32, 0x2D }, /* PRESENTATION FORM FOR VERTICAL EN DASH -> '-' */
+ { 0x33, 0x5F }, /* PRESENTATION FORM FOR VERTICAL LOW LINE -> '_' */
+ { 0x34, 0x5F }, /* PRESENTATION FORM FOR VERTICAL WAVY LOW LINE -> '_' */
+ { 0x35, 0x28 }, /* PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS -> '(' */
+ { 0x37, 0x7B }, /* PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET -> '{' */
+ { 0x39, 0x5B }, /* PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET -> '[' */
+ { 0x3F, 0x3C }, /* PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET -> '<' */
+ { 0x41, 0x5B }, /* PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET -> '[' */
+ { 0x43, 0x7B }, /* PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET -> '{' */
+ { 0x44, 0x7D }, /* PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET -> '}' */
+ { 0x50, 0x2C }, /* SMALL COMMA -> ',' */
+ { 0x51, 0x2C }, /* SMALL IDEOGRAPHIC COMMA -> ',' */
+ { 0x52, 0x2E }, /* SMALL FULL STOP -> '.' */
+ { 0x54, 0x3B }, /* SMALL SEMICOLON -> ';' */
+ { 0x55, 0x3A }, /* SMALL COLON -> ':' */
+ { 0x56, 0x3F }, /* SMALL QUESTION MARK -> '?' */
+ { 0x57, 0x21 }, /* SMALL EXCLAMATION MARK -> '!' */
+ { 0x58, 0x2D }, /* SMALL EM DASH -> '-' */
+ { 0x59, 0x28 }, /* SMALL LEFT PARENTHESIS -> '(' */
+ { 0x5A, 0x29 }, /* SMALL RIGHT PARENTHESIS -> ')' */
+ { 0x5B, 0x7B }, /* SMALL LEFT CURLY BRACKET -> '{' */
+ { 0x5C, 0x7D }, /* SMALL RIGHT CURLY BRACKET -> '}' */
+ { 0x5D, 0x7B }, /* SMALL LEFT TORTOISE SHELL BRACKET -> '{' */
+ { 0x5E, 0x7D }, /* SMALL RIGHT TORTOISE SHELL BRACKET -> '}' */
+ { 0x5F, 0x23 }, /* SMALL NUMBER SIGN -> '#' */
+ { 0x60, 0x26 }, /* SMALL AMPERSAND -> '&' */
+ { 0x61, 0x2A }, /* SMALL ASTERISK -> '*' */
+ { 0x62, 0x2B }, /* SMALL PLUS SIGN -> '+' */
+ { 0x63, 0x2D }, /* SMALL HYPHEN-MINUS -> '-' */
+ { 0x64, 0x3C }, /* SMALL LESS-THAN SIGN -> '<' */
+ { 0x65, 0x3E }, /* SMALL GREATER-THAN SIGN -> '>' */
+ { 0x66, 0x3D }, /* SMALL EQUALS SIGN -> '=' */
+ { 0x68, 0x5C }, /* SMALL REVERSE SOLIDUS -> '\' */
+ { 0x69, 0x24 }, /* SMALL DOLLAR SIGN -> '$' */
+ { 0x6A, 0x25 }, /* SMALL PERCENT SIGN -> '%' */
+ { 0x6B, 0x40 }, /* SMALL COMMERCIAL AT -> '@' */
+ /* Entries for page 0xFF */
+ { 0x61, 0x2E }, /* HALFWIDTH IDEOGRAPHIC FULL STOP -> '.' */
+ { 0x62, 0x5B }, /* HALFWIDTH LEFT CORNER BRACKET -> '[' */
+ { 0x63, 0x5D }, /* HALFWIDTH RIGHT CORNER BRACKET -> ']' */
+ { 0x64, 0x2C }, /* HALFWIDTH IDEOGRAPHIC COMMA -> ',' */
+ { 0x65, 0x2A }, /* HALFWIDTH KATAKANA MIDDLE DOT -> '*' */
+ { 0x67, 0x61 }, /* HALFWIDTH KATAKANA LETTER SMALL A -> 'a' */
+ { 0x68, 0x69 }, /* HALFWIDTH KATAKANA LETTER SMALL I -> 'i' */
+ { 0x69, 0x75 }, /* HALFWIDTH KATAKANA LETTER SMALL U -> 'u' */
+ { 0x6A, 0x65 }, /* HALFWIDTH KATAKANA LETTER SMALL E -> 'e' */
+ { 0x6B, 0x6F }, /* HALFWIDTH KATAKANA LETTER SMALL O -> 'o' */
+ { 0x70, 0x2B }, /* HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK -> '+' */
+ { 0x71, 0x61 }, /* HALFWIDTH KATAKANA LETTER A -> 'a' */
+ { 0x72, 0x69 }, /* HALFWIDTH KATAKANA LETTER I -> 'i' */
+ { 0x73, 0x75 }, /* HALFWIDTH KATAKANA LETTER U -> 'u' */
+ { 0x74, 0x65 }, /* HALFWIDTH KATAKANA LETTER E -> 'e' */
+ { 0x75, 0x6F }, /* HALFWIDTH KATAKANA LETTER O -> 'o' */
+ { 0x9D, 0x6E }, /* HALFWIDTH KATAKANA LETTER N -> 'n' */
+ { 0x9E, 0x3A }, /* HALFWIDTH KATAKANA VOICED SOUND MARK -> ':' */
+ { 0x9F, 0x3B }, /* HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK -> ';' */
+ { 0xA1, 0x67 }, /* HALFWIDTH HANGUL LETTER KIYEOK -> 'g' */
+ { 0xA4, 0x6E }, /* HALFWIDTH HANGUL LETTER NIEUN -> 'n' */
+ { 0xA7, 0x64 }, /* HALFWIDTH HANGUL LETTER TIKEUT -> 'd' */
+ { 0xA9, 0x72 }, /* HALFWIDTH HANGUL LETTER RIEUL -> 'r' */
+ { 0xB1, 0x6D }, /* HALFWIDTH HANGUL LETTER MIEUM -> 'm' */
+ { 0xB2, 0x62 }, /* HALFWIDTH HANGUL LETTER PIEUP -> 'b' */
+ { 0xB5, 0x73 }, /* HALFWIDTH HANGUL LETTER SIOS -> 's' */
+ { 0xB8, 0x6A }, /* HALFWIDTH HANGUL LETTER CIEUC -> 'j' */
+ { 0xBA, 0x63 }, /* HALFWIDTH HANGUL LETTER CHIEUCH -> 'c' */
+ { 0xBB, 0x6B }, /* HALFWIDTH HANGUL LETTER KHIEUKH -> 'k' */
+ { 0xBC, 0x74 }, /* HALFWIDTH HANGUL LETTER THIEUTH -> 't' */
+ { 0xBD, 0x70 }, /* HALFWIDTH HANGUL LETTER PHIEUPH -> 'p' */
+ { 0xBE, 0x68 }, /* HALFWIDTH HANGUL LETTER HIEUH -> 'h' */
+ { 0xC2, 0x61 }, /* HALFWIDTH HANGUL LETTER A -> 'a' */
+ { 0xC7, 0x65 }, /* HALFWIDTH HANGUL LETTER E -> 'e' */
+ { 0xCC, 0x6F }, /* HALFWIDTH HANGUL LETTER O -> 'o' */
+ { 0xD3, 0x75 }, /* HALFWIDTH HANGUL LETTER U -> 'u' */
+ { 0xDC, 0x69 }, /* HALFWIDTH HANGUL LETTER I -> 'i' */
+ { 0xE2, 0x21 }, /* FULLWIDTH NOT SIGN -> '!' */
+ { 0xE3, 0x2D }, /* FULLWIDTH MACRON -> '-' */
+ { 0xE4, 0x7C }, /* FULLWIDTH BROKEN BAR -> '|' */
+ { 0xE8, 0x7C }, /* HALFWIDTH FORMS LIGHT VERTICAL -> '|' */
+ { 0xE9, 0x3C }, /* HALFWIDTH LEFTWARDS ARROW -> '<' */
+ { 0xEA, 0x5E }, /* HALFWIDTH UPWARDS ARROW -> '^' */
+ { 0xEB, 0x3E }, /* HALFWIDTH RIGHTWARDS ARROW -> '>' */
+ { 0xEC, 0x76 }, /* HALFWIDTH DOWNWARDS ARROW -> 'v' */
+ { 0xED, 0x23 }, /* HALFWIDTH BLACK SQUARE -> '#' */
+ { 0xEE, 0x4F }, /* HALFWIDTH WHITE CIRCLE -> 'O' */
+ { 0xF9, 0x7B }, /* INTERLINEAR ANNOTATION ANCHOR -> '{' */
+ { 0xFA, 0x7C }, /* INTERLINEAR ANNOTATION SEPARATOR -> '|' */
+ { 0xFB, 0x7D }, /* INTERLINEAR ANNOTATION TERMINATOR -> '}' */
+};
+
+#define UCS_PAGE_ENTRY_RANGE_MARKER 0
diff --git a/drivers/tty/vt/ucs_recompose_table.h_shipped b/drivers/tty/vt/ucs_recompose_table.h_shipped
new file mode 100644
index 000000000..bd91edde5
--- /dev/null
+++ b/drivers/tty/vt/ucs_recompose_table.h_shipped
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ucs_recompose_table.h - Unicode character recomposition
+ *
+ * Auto-generated by gen_ucs_recompose_table.py
+ *
+ * Unicode Version: 16.0.0
+ *
+ * This file contains a table with most commonly used Latin, Greek, and
+ * Cyrillic recomposition pairs only (71 entries). To generate a table with
+ * all possible recomposition pairs from the Unicode BMP (1000 entries)
+ * instead, run:
+ *
+ * python gen_ucs_recompose_table.py --full
+ */
+
+/*
+ * Table of most commonly used Latin, Greek, and Cyrillic recomposition pairs only
+ * Sorted by base character and then combining mark for binary search
+ */
+static const struct ucs_recomposition ucs_recomposition_table[] = {
+ { 0x0041, 0x0300, 0x00C0 }, /* LATIN CAPITAL LETTER A + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER A WITH GRAVE */
+ { 0x0041, 0x0301, 0x00C1 }, /* LATIN CAPITAL LETTER A + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER A WITH ACUTE */
+ { 0x0041, 0x0302, 0x00C2 }, /* LATIN CAPITAL LETTER A + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
+ { 0x0041, 0x0303, 0x00C3 }, /* LATIN CAPITAL LETTER A + COMBINING TILDE = LATIN CAPITAL LETTER A WITH TILDE */
+ { 0x0041, 0x0308, 0x00C4 }, /* LATIN CAPITAL LETTER A + COMBINING DIAERESIS = LATIN CAPITAL LETTER A WITH DIAERESIS */
+ { 0x0041, 0x030A, 0x00C5 }, /* LATIN CAPITAL LETTER A + COMBINING RING ABOVE = LATIN CAPITAL LETTER A WITH RING ABOVE */
+ { 0x0043, 0x0327, 0x00C7 }, /* LATIN CAPITAL LETTER C + COMBINING CEDILLA = LATIN CAPITAL LETTER C WITH CEDILLA */
+ { 0x0045, 0x0300, 0x00C8 }, /* LATIN CAPITAL LETTER E + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER E WITH GRAVE */
+ { 0x0045, 0x0301, 0x00C9 }, /* LATIN CAPITAL LETTER E + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER E WITH ACUTE */
+ { 0x0045, 0x0302, 0x00CA }, /* LATIN CAPITAL LETTER E + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
+ { 0x0045, 0x0308, 0x00CB }, /* LATIN CAPITAL LETTER E + COMBINING DIAERESIS = LATIN CAPITAL LETTER E WITH DIAERESIS */
+ { 0x0049, 0x0300, 0x00CC }, /* LATIN CAPITAL LETTER I + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER I WITH GRAVE */
+ { 0x0049, 0x0301, 0x00CD }, /* LATIN CAPITAL LETTER I + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER I WITH ACUTE */
+ { 0x0049, 0x0302, 0x00CE }, /* LATIN CAPITAL LETTER I + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
+ { 0x0049, 0x0308, 0x00CF }, /* LATIN CAPITAL LETTER I + COMBINING DIAERESIS = LATIN CAPITAL LETTER I WITH DIAERESIS */
+ { 0x004E, 0x0303, 0x00D1 }, /* LATIN CAPITAL LETTER N + COMBINING TILDE = LATIN CAPITAL LETTER N WITH TILDE */
+ { 0x004F, 0x0300, 0x00D2 }, /* LATIN CAPITAL LETTER O + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER O WITH GRAVE */
+ { 0x004F, 0x0301, 0x00D3 }, /* LATIN CAPITAL LETTER O + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER O WITH ACUTE */
+ { 0x004F, 0x0302, 0x00D4 }, /* LATIN CAPITAL LETTER O + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
+ { 0x004F, 0x0303, 0x00D5 }, /* LATIN CAPITAL LETTER O + COMBINING TILDE = LATIN CAPITAL LETTER O WITH TILDE */
+ { 0x004F, 0x0308, 0x00D6 }, /* LATIN CAPITAL LETTER O + COMBINING DIAERESIS = LATIN CAPITAL LETTER O WITH DIAERESIS */
+ { 0x0055, 0x0300, 0x00D9 }, /* LATIN CAPITAL LETTER U + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER U WITH GRAVE */
+ { 0x0055, 0x0301, 0x00DA }, /* LATIN CAPITAL LETTER U + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER U WITH ACUTE */
+ { 0x0055, 0x0302, 0x00DB }, /* LATIN CAPITAL LETTER U + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
+ { 0x0055, 0x0308, 0x00DC }, /* LATIN CAPITAL LETTER U + COMBINING DIAERESIS = LATIN CAPITAL LETTER U WITH DIAERESIS */
+ { 0x0059, 0x0301, 0x00DD }, /* LATIN CAPITAL LETTER Y + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER Y WITH ACUTE */
+ { 0x0061, 0x0300, 0x00E0 }, /* LATIN SMALL LETTER A + COMBINING GRAVE ACCENT = LATIN SMALL LETTER A WITH GRAVE */
+ { 0x0061, 0x0301, 0x00E1 }, /* LATIN SMALL LETTER A + COMBINING ACUTE ACCENT = LATIN SMALL LETTER A WITH ACUTE */
+ { 0x0061, 0x0302, 0x00E2 }, /* LATIN SMALL LETTER A + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER A WITH CIRCUMFLEX */
+ { 0x0061, 0x0303, 0x00E3 }, /* LATIN SMALL LETTER A + COMBINING TILDE = LATIN SMALL LETTER A WITH TILDE */
+ { 0x0061, 0x0308, 0x00E4 }, /* LATIN SMALL LETTER A + COMBINING DIAERESIS = LATIN SMALL LETTER A WITH DIAERESIS */
+ { 0x0061, 0x030A, 0x00E5 }, /* LATIN SMALL LETTER A + COMBINING RING ABOVE = LATIN SMALL LETTER A WITH RING ABOVE */
+ { 0x0063, 0x0327, 0x00E7 }, /* LATIN SMALL LETTER C + COMBINING CEDILLA = LATIN SMALL LETTER C WITH CEDILLA */
+ { 0x0065, 0x0300, 0x00E8 }, /* LATIN SMALL LETTER E + COMBINING GRAVE ACCENT = LATIN SMALL LETTER E WITH GRAVE */
+ { 0x0065, 0x0301, 0x00E9 }, /* LATIN SMALL LETTER E + COMBINING ACUTE ACCENT = LATIN SMALL LETTER E WITH ACUTE */
+ { 0x0065, 0x0302, 0x00EA }, /* LATIN SMALL LETTER E + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER E WITH CIRCUMFLEX */
+ { 0x0065, 0x0308, 0x00EB }, /* LATIN SMALL LETTER E + COMBINING DIAERESIS = LATIN SMALL LETTER E WITH DIAERESIS */
+ { 0x0069, 0x0300, 0x00EC }, /* LATIN SMALL LETTER I + COMBINING GRAVE ACCENT = LATIN SMALL LETTER I WITH GRAVE */
+ { 0x0069, 0x0301, 0x00ED }, /* LATIN SMALL LETTER I + COMBINING ACUTE ACCENT = LATIN SMALL LETTER I WITH ACUTE */
+ { 0x0069, 0x0302, 0x00EE }, /* LATIN SMALL LETTER I + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER I WITH CIRCUMFLEX */
+ { 0x0069, 0x0308, 0x00EF }, /* LATIN SMALL LETTER I + COMBINING DIAERESIS = LATIN SMALL LETTER I WITH DIAERESIS */
+ { 0x006E, 0x0303, 0x00F1 }, /* LATIN SMALL LETTER N + COMBINING TILDE = LATIN SMALL LETTER N WITH TILDE */
+ { 0x006F, 0x0300, 0x00F2 }, /* LATIN SMALL LETTER O + COMBINING GRAVE ACCENT = LATIN SMALL LETTER O WITH GRAVE */
+ { 0x006F, 0x0301, 0x00F3 }, /* LATIN SMALL LETTER O + COMBINING ACUTE ACCENT = LATIN SMALL LETTER O WITH ACUTE */
+ { 0x006F, 0x0302, 0x00F4 }, /* LATIN SMALL LETTER O + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER O WITH CIRCUMFLEX */
+ { 0x006F, 0x0303, 0x00F5 }, /* LATIN SMALL LETTER O + COMBINING TILDE = LATIN SMALL LETTER O WITH TILDE */
+ { 0x006F, 0x0308, 0x00F6 }, /* LATIN SMALL LETTER O + COMBINING DIAERESIS = LATIN SMALL LETTER O WITH DIAERESIS */
+ { 0x0075, 0x0300, 0x00F9 }, /* LATIN SMALL LETTER U + COMBINING GRAVE ACCENT = LATIN SMALL LETTER U WITH GRAVE */
+ { 0x0075, 0x0301, 0x00FA }, /* LATIN SMALL LETTER U + COMBINING ACUTE ACCENT = LATIN SMALL LETTER U WITH ACUTE */
+ { 0x0075, 0x0302, 0x00FB }, /* LATIN SMALL LETTER U + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER U WITH CIRCUMFLEX */
+ { 0x0075, 0x0308, 0x00FC }, /* LATIN SMALL LETTER U + COMBINING DIAERESIS = LATIN SMALL LETTER U WITH DIAERESIS */
+ { 0x0079, 0x0301, 0x00FD }, /* LATIN SMALL LETTER Y + COMBINING ACUTE ACCENT = LATIN SMALL LETTER Y WITH ACUTE */
+ { 0x0079, 0x0308, 0x00FF }, /* LATIN SMALL LETTER Y + COMBINING DIAERESIS = LATIN SMALL LETTER Y WITH DIAERESIS */
+ { 0x0391, 0x0301, 0x0386 }, /* GREEK CAPITAL LETTER ALPHA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ALPHA WITH TONOS */
+ { 0x0395, 0x0301, 0x0388 }, /* GREEK CAPITAL LETTER EPSILON + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER EPSILON WITH TONOS */
+ { 0x0397, 0x0301, 0x0389 }, /* GREEK CAPITAL LETTER ETA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ETA WITH TONOS */
+ { 0x0399, 0x0301, 0x038A }, /* GREEK CAPITAL LETTER IOTA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER IOTA WITH TONOS */
+ { 0x039F, 0x0301, 0x038C }, /* GREEK CAPITAL LETTER OMICRON + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMICRON WITH TONOS */
+ { 0x03A5, 0x0301, 0x038E }, /* GREEK CAPITAL LETTER UPSILON + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER UPSILON WITH TONOS */
+ { 0x03A9, 0x0301, 0x038F }, /* GREEK CAPITAL LETTER OMEGA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMEGA WITH TONOS */
+ { 0x03B1, 0x0301, 0x03AC }, /* GREEK SMALL LETTER ALPHA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ALPHA WITH TONOS */
+ { 0x03B5, 0x0301, 0x03AD }, /* GREEK SMALL LETTER EPSILON + COMBINING ACUTE ACCENT = GREEK SMALL LETTER EPSILON WITH TONOS */
+ { 0x03B7, 0x0301, 0x03AE }, /* GREEK SMALL LETTER ETA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ETA WITH TONOS */
+ { 0x03B9, 0x0301, 0x03AF }, /* GREEK SMALL LETTER IOTA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER IOTA WITH TONOS */
+ { 0x03BF, 0x0301, 0x03CC }, /* GREEK SMALL LETTER OMICRON + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMICRON WITH TONOS */
+ { 0x03C5, 0x0301, 0x03CD }, /* GREEK SMALL LETTER UPSILON + COMBINING ACUTE ACCENT = GREEK SMALL LETTER UPSILON WITH TONOS */
+ { 0x03C9, 0x0301, 0x03CE }, /* GREEK SMALL LETTER OMEGA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMEGA WITH TONOS */
+ { 0x0418, 0x0306, 0x0419 }, /* CYRILLIC CAPITAL LETTER I + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT I */
+ { 0x0423, 0x0306, 0x040E }, /* CYRILLIC CAPITAL LETTER U + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT U */
+ { 0x0438, 0x0306, 0x0439 }, /* CYRILLIC SMALL LETTER I + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT I */
+ { 0x0443, 0x0306, 0x045E }, /* CYRILLIC SMALL LETTER U + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT U */
+};
+
+/*
+ * Boundary values for quick rejection
+ * These are calculated by analyzing the table during generation
+ */
+#define UCS_RECOMPOSE_MIN_BASE 0x0041
+#define UCS_RECOMPOSE_MAX_BASE 0x0443
+#define UCS_RECOMPOSE_MIN_MARK 0x0300
+#define UCS_RECOMPOSE_MAX_MARK 0x0327
diff --git a/drivers/tty/vt/ucs_width_table.h_shipped b/drivers/tty/vt/ucs_width_table.h_shipped
new file mode 100644
index 000000000..5cd6434bf
--- /dev/null
+++ b/drivers/tty/vt/ucs_width_table.h_shipped
@@ -0,0 +1,490 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ucs_width_table.h - Unicode character width
+ *
+ * Auto-generated by gen_ucs_width_table.py
+ *
+ * Unicode Version: 16.0.0
+ *
+ * Zero-width and double-width ranges are merged into one sorted-by-`first`
+ * table per region. The non-BMP table additionally hosts the BMP
+ * double-width bitmap in the low 8 bits of `last` of its
+ * first 33 entries (covering 262 BMP entries).
+ * See ucs.c for the encoding details and the lookup code.
+ */
+
+/* Bits per BMP-bitmap chunk hosted in one non-BMP entry's `last` field. */
+#define UCS_NONBMP_BMP_BITS 8
+
+/* Combined zero- and double-width ranges
+ * (BMP - Basic Multilingual Plane, U+0000 to U+FFFF). */
+static const struct ucs_width16 ucs_bmp_ranges[] = {
+ { BMP_0WIDTH(0x00AD, 0x00AD) }, /* SOFT HYPHEN */
+ { BMP_0WIDTH(0x0300, 0x036F) }, /* COMBINING GRAVE ACCENT - COMBINING LATIN SMALL LETTER X */
+ { BMP_0WIDTH(0x0483, 0x0489) }, /* COMBINING CYRILLIC TITLO - COMBINING CYRILLIC MILLIONS SIGN */
+ { BMP_0WIDTH(0x0591, 0x05BD) }, /* HEBREW ACCENT ETNAHTA - HEBREW POINT METEG */
+ { BMP_0WIDTH(0x05BF, 0x05BF) }, /* HEBREW POINT RAFE */
+ { BMP_0WIDTH(0x05C1, 0x05C2) }, /* HEBREW POINT SHIN DOT - HEBREW POINT SIN DOT */
+ { BMP_0WIDTH(0x05C4, 0x05C5) }, /* HEBREW MARK UPPER DOT - HEBREW MARK LOWER DOT */
+ { BMP_0WIDTH(0x05C7, 0x05C7) }, /* HEBREW POINT QAMATS QATAN */
+ { BMP_0WIDTH(0x0600, 0x0605) }, /* ARABIC NUMBER SIGN - ARABIC NUMBER MARK ABOVE */
+ { BMP_0WIDTH(0x0610, 0x061A) }, /* ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM - ARABIC SMALL KASRA */
+ { BMP_0WIDTH(0x061C, 0x061C) }, /* ARABIC LETTER MARK */
+ { BMP_0WIDTH(0x064B, 0x065F) }, /* ARABIC FATHATAN - ARABIC WAVY HAMZA BELOW */
+ { BMP_0WIDTH(0x0670, 0x0670) }, /* ARABIC LETTER SUPERSCRIPT ALEF */
+ { BMP_0WIDTH(0x06D6, 0x06DD) }, /* ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA - ARABIC END OF AYAH */
+ { BMP_0WIDTH(0x06DF, 0x06E4) }, /* ARABIC SMALL HIGH ROUNDED ZERO - ARABIC SMALL HIGH MADDA */
+ { BMP_0WIDTH(0x06E7, 0x06E8) }, /* ARABIC SMALL HIGH YEH - ARABIC SMALL HIGH NOON */
+ { BMP_0WIDTH(0x06EA, 0x06ED) }, /* ARABIC EMPTY CENTRE LOW STOP - ARABIC SMALL LOW MEEM */
+ { BMP_0WIDTH(0x070F, 0x070F) }, /* SYRIAC ABBREVIATION MARK */
+ { BMP_0WIDTH(0x0711, 0x0711) }, /* SYRIAC LETTER SUPERSCRIPT ALAPH */
+ { BMP_0WIDTH(0x0730, 0x074A) }, /* SYRIAC PTHAHA ABOVE - SYRIAC BARREKH */
+ { BMP_0WIDTH(0x07A6, 0x07B0) }, /* THAANA ABAFILI - THAANA SUKUN */
+ { BMP_0WIDTH(0x07EB, 0x07F3) }, /* NKO COMBINING SHORT HIGH TONE - NKO COMBINING DOUBLE DOT ABOVE */
+ { BMP_0WIDTH(0x07FD, 0x07FD) }, /* NKO DANTAYALAN */
+ { BMP_0WIDTH(0x0816, 0x0819) }, /* SAMARITAN MARK IN - SAMARITAN MARK DAGESH */
+ { BMP_0WIDTH(0x081B, 0x0823) }, /* SAMARITAN MARK EPENTHETIC YUT - SAMARITAN VOWEL SIGN A */
+ { BMP_0WIDTH(0x0825, 0x0827) }, /* SAMARITAN VOWEL SIGN SHORT A - SAMARITAN VOWEL SIGN U */
+ { BMP_0WIDTH(0x0829, 0x082D) }, /* SAMARITAN VOWEL SIGN LONG I - SAMARITAN MARK NEQUDAA */
+ { BMP_0WIDTH(0x0859, 0x085B) }, /* MANDAIC AFFRICATION MARK - MANDAIC GEMINATION MARK */
+ { BMP_0WIDTH(0x0890, 0x0891) }, /* ARABIC POUND MARK ABOVE - ARABIC PIASTRE MARK ABOVE */
+ { BMP_0WIDTH(0x0897, 0x089F) }, /* ARABIC PEPET - ARABIC HALF MADDA OVER MADDA */
+ { BMP_0WIDTH(0x08CA, 0x0903) }, /* ARABIC SMALL HIGH FARSI YEH - DEVANAGARI SIGN VISARGA */
+ { BMP_0WIDTH(0x093A, 0x093C) }, /* DEVANAGARI VOWEL SIGN OE - DEVANAGARI SIGN NUKTA */
+ { BMP_0WIDTH(0x093E, 0x094F) }, /* DEVANAGARI VOWEL SIGN AA - DEVANAGARI VOWEL SIGN AW */
+ { BMP_0WIDTH(0x0951, 0x0957) }, /* DEVANAGARI STRESS SIGN UDATTA - DEVANAGARI VOWEL SIGN UUE */
+ { BMP_0WIDTH(0x0962, 0x0963) }, /* DEVANAGARI VOWEL SIGN VOCALIC L - DEVANAGARI VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x0981, 0x0983) }, /* BENGALI SIGN CANDRABINDU - BENGALI SIGN VISARGA */
+ { BMP_0WIDTH(0x09BC, 0x09BC) }, /* BENGALI SIGN NUKTA */
+ { BMP_0WIDTH(0x09BE, 0x09C4) }, /* BENGALI VOWEL SIGN AA - BENGALI VOWEL SIGN VOCALIC RR */
+ { BMP_0WIDTH(0x09C7, 0x09C8) }, /* BENGALI VOWEL SIGN E - BENGALI VOWEL SIGN AI */
+ { BMP_0WIDTH(0x09CB, 0x09CD) }, /* BENGALI VOWEL SIGN O - BENGALI SIGN VIRAMA */
+ { BMP_0WIDTH(0x09D7, 0x09D7) }, /* BENGALI AU LENGTH MARK */
+ { BMP_0WIDTH(0x09E2, 0x09E3) }, /* BENGALI VOWEL SIGN VOCALIC L - BENGALI VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x09FE, 0x09FE) }, /* BENGALI SANDHI MARK */
+ { BMP_0WIDTH(0x0A01, 0x0A03) }, /* GURMUKHI SIGN ADAK BINDI - GURMUKHI SIGN VISARGA */
+ { BMP_0WIDTH(0x0A3C, 0x0A3C) }, /* GURMUKHI SIGN NUKTA */
+ { BMP_0WIDTH(0x0A3E, 0x0A42) }, /* GURMUKHI VOWEL SIGN AA - GURMUKHI VOWEL SIGN UU */
+ { BMP_0WIDTH(0x0A47, 0x0A48) }, /* GURMUKHI VOWEL SIGN EE - GURMUKHI VOWEL SIGN AI */
+ { BMP_0WIDTH(0x0A4B, 0x0A4D) }, /* GURMUKHI VOWEL SIGN OO - GURMUKHI SIGN VIRAMA */
+ { BMP_0WIDTH(0x0A51, 0x0A51) }, /* GURMUKHI SIGN UDAAT */
+ { BMP_0WIDTH(0x0A70, 0x0A71) }, /* GURMUKHI TIPPI - GURMUKHI ADDAK */
+ { BMP_0WIDTH(0x0A75, 0x0A75) }, /* GURMUKHI SIGN YAKASH */
+ { BMP_0WIDTH(0x0A81, 0x0A83) }, /* GUJARATI SIGN CANDRABINDU - GUJARATI SIGN VISARGA */
+ { BMP_0WIDTH(0x0ABC, 0x0ABC) }, /* GUJARATI SIGN NUKTA */
+ { BMP_0WIDTH(0x0ABE, 0x0AC5) }, /* GUJARATI VOWEL SIGN AA - GUJARATI VOWEL SIGN CANDRA E */
+ { BMP_0WIDTH(0x0AC7, 0x0AC9) }, /* GUJARATI VOWEL SIGN E - GUJARATI VOWEL SIGN CANDRA O */
+ { BMP_0WIDTH(0x0ACB, 0x0ACD) }, /* GUJARATI VOWEL SIGN O - GUJARATI SIGN VIRAMA */
+ { BMP_0WIDTH(0x0AE2, 0x0AE3) }, /* GUJARATI VOWEL SIGN VOCALIC L - GUJARATI VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x0AFA, 0x0AFF) }, /* GUJARATI SIGN SUKUN - GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE */
+ { BMP_0WIDTH(0x0B01, 0x0B03) }, /* ORIYA SIGN CANDRABINDU - ORIYA SIGN VISARGA */
+ { BMP_0WIDTH(0x0B3C, 0x0B3C) }, /* ORIYA SIGN NUKTA */
+ { BMP_0WIDTH(0x0B3E, 0x0B44) }, /* ORIYA VOWEL SIGN AA - ORIYA VOWEL SIGN VOCALIC RR */
+ { BMP_0WIDTH(0x0B47, 0x0B48) }, /* ORIYA VOWEL SIGN E - ORIYA VOWEL SIGN AI */
+ { BMP_0WIDTH(0x0B4B, 0x0B4D) }, /* ORIYA VOWEL SIGN O - ORIYA SIGN VIRAMA */
+ { BMP_0WIDTH(0x0B55, 0x0B57) }, /* ORIYA SIGN OVERLINE - ORIYA AU LENGTH MARK */
+ { BMP_0WIDTH(0x0B62, 0x0B63) }, /* ORIYA VOWEL SIGN VOCALIC L - ORIYA VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x0B82, 0x0B82) }, /* TAMIL SIGN ANUSVARA */
+ { BMP_0WIDTH(0x0BBE, 0x0BC2) }, /* TAMIL VOWEL SIGN AA - TAMIL VOWEL SIGN UU */
+ { BMP_0WIDTH(0x0BC6, 0x0BC8) }, /* TAMIL VOWEL SIGN E - TAMIL VOWEL SIGN AI */
+ { BMP_0WIDTH(0x0BCA, 0x0BCD) }, /* TAMIL VOWEL SIGN O - TAMIL SIGN VIRAMA */
+ { BMP_0WIDTH(0x0BD7, 0x0BD7) }, /* TAMIL AU LENGTH MARK */
+ { BMP_0WIDTH(0x0C00, 0x0C04) }, /* TELUGU SIGN COMBINING CANDRABINDU ABOVE - TELUGU SIGN COMBINING ANUSVARA ABOVE */
+ { BMP_0WIDTH(0x0C3C, 0x0C3C) }, /* TELUGU SIGN NUKTA */
+ { BMP_0WIDTH(0x0C3E, 0x0C44) }, /* TELUGU VOWEL SIGN AA - TELUGU VOWEL SIGN VOCALIC RR */
+ { BMP_0WIDTH(0x0C46, 0x0C48) }, /* TELUGU VOWEL SIGN E - TELUGU VOWEL SIGN AI */
+ { BMP_0WIDTH(0x0C4A, 0x0C4D) }, /* TELUGU VOWEL SIGN O - TELUGU SIGN VIRAMA */
+ { BMP_0WIDTH(0x0C55, 0x0C56) }, /* TELUGU LENGTH MARK - TELUGU AI LENGTH MARK */
+ { BMP_0WIDTH(0x0C62, 0x0C63) }, /* TELUGU VOWEL SIGN VOCALIC L - TELUGU VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x0C81, 0x0C83) }, /* KANNADA SIGN CANDRABINDU - KANNADA SIGN VISARGA */
+ { BMP_0WIDTH(0x0CBC, 0x0CBC) }, /* KANNADA SIGN NUKTA */
+ { BMP_0WIDTH(0x0CBE, 0x0CC4) }, /* KANNADA VOWEL SIGN AA - KANNADA VOWEL SIGN VOCALIC RR */
+ { BMP_0WIDTH(0x0CC6, 0x0CC8) }, /* KANNADA VOWEL SIGN E - KANNADA VOWEL SIGN AI */
+ { BMP_0WIDTH(0x0CCA, 0x0CCD) }, /* KANNADA VOWEL SIGN O - KANNADA SIGN VIRAMA */
+ { BMP_0WIDTH(0x0CD5, 0x0CD6) }, /* KANNADA LENGTH MARK - KANNADA AI LENGTH MARK */
+ { BMP_0WIDTH(0x0CE2, 0x0CE3) }, /* KANNADA VOWEL SIGN VOCALIC L - KANNADA VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x0CF3, 0x0CF3) }, /* KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT */
+ { BMP_0WIDTH(0x0D00, 0x0D03) }, /* MALAYALAM SIGN COMBINING ANUSVARA ABOVE - MALAYALAM SIGN VISARGA */
+ { BMP_0WIDTH(0x0D3B, 0x0D3C) }, /* MALAYALAM SIGN VERTICAL BAR VIRAMA - MALAYALAM SIGN CIRCULAR VIRAMA */
+ { BMP_0WIDTH(0x0D3E, 0x0D44) }, /* MALAYALAM VOWEL SIGN AA - MALAYALAM VOWEL SIGN VOCALIC RR */
+ { BMP_0WIDTH(0x0D46, 0x0D48) }, /* MALAYALAM VOWEL SIGN E - MALAYALAM VOWEL SIGN AI */
+ { BMP_0WIDTH(0x0D4A, 0x0D4D) }, /* MALAYALAM VOWEL SIGN O - MALAYALAM SIGN VIRAMA */
+ { BMP_0WIDTH(0x0D57, 0x0D57) }, /* MALAYALAM AU LENGTH MARK */
+ { BMP_0WIDTH(0x0D62, 0x0D63) }, /* MALAYALAM VOWEL SIGN VOCALIC L - MALAYALAM VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x0D81, 0x0D83) }, /* SINHALA SIGN CANDRABINDU - SINHALA SIGN VISARGAYA */
+ { BMP_0WIDTH(0x0DCA, 0x0DCA) }, /* SINHALA SIGN AL-LAKUNA */
+ { BMP_0WIDTH(0x0DCF, 0x0DD4) }, /* SINHALA VOWEL SIGN AELA-PILLA - SINHALA VOWEL SIGN KETTI PAA-PILLA */
+ { BMP_0WIDTH(0x0DD6, 0x0DD6) }, /* SINHALA VOWEL SIGN DIGA PAA-PILLA */
+ { BMP_0WIDTH(0x0DD8, 0x0DDF) }, /* SINHALA VOWEL SIGN GAETTA-PILLA - SINHALA VOWEL SIGN GAYANUKITTA */
+ { BMP_0WIDTH(0x0DF2, 0x0DF3) }, /* SINHALA VOWEL SIGN DIGA GAETTA-PILLA - SINHALA VOWEL SIGN DIGA GAYANUKITTA */
+ { BMP_0WIDTH(0x0E31, 0x0E31) }, /* THAI CHARACTER MAI HAN-AKAT */
+ { BMP_0WIDTH(0x0E34, 0x0E3A) }, /* THAI CHARACTER SARA I - THAI CHARACTER PHINTHU */
+ { BMP_0WIDTH(0x0E47, 0x0E4E) }, /* THAI CHARACTER MAITAIKHU - THAI CHARACTER YAMAKKAN */
+ { BMP_0WIDTH(0x0EB1, 0x0EB1) }, /* LAO VOWEL SIGN MAI KAN */
+ { BMP_0WIDTH(0x0EB4, 0x0EBC) }, /* LAO VOWEL SIGN I - LAO SEMIVOWEL SIGN LO */
+ { BMP_0WIDTH(0x0EC8, 0x0ECE) }, /* LAO TONE MAI EK - LAO YAMAKKAN */
+ { BMP_0WIDTH(0x0F18, 0x0F19) }, /* TIBETAN ASTROLOGICAL SIGN -KHYUD PA - TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS */
+ { BMP_0WIDTH(0x0F35, 0x0F35) }, /* TIBETAN MARK NGAS BZUNG NYI ZLA */
+ { BMP_0WIDTH(0x0F37, 0x0F37) }, /* TIBETAN MARK NGAS BZUNG SGOR RTAGS */
+ { BMP_0WIDTH(0x0F39, 0x0F39) }, /* TIBETAN MARK TSA -PHRU */
+ { BMP_0WIDTH(0x0F3E, 0x0F3F) }, /* TIBETAN SIGN YAR TSHES - TIBETAN SIGN MAR TSHES */
+ { BMP_0WIDTH(0x0F71, 0x0F84) }, /* TIBETAN VOWEL SIGN AA - TIBETAN MARK HALANTA */
+ { BMP_0WIDTH(0x0F86, 0x0F87) }, /* TIBETAN SIGN LCI RTAGS - TIBETAN SIGN YANG RTAGS */
+ { BMP_0WIDTH(0x0F8D, 0x0F97) }, /* TIBETAN SUBJOINED SIGN LCE TSA CAN - TIBETAN SUBJOINED LETTER JA */
+ { BMP_0WIDTH(0x0F99, 0x0FBC) }, /* TIBETAN SUBJOINED LETTER NYA - TIBETAN SUBJOINED LETTER FIXED-FORM RA */
+ { BMP_0WIDTH(0x0FC6, 0x0FC6) }, /* TIBETAN SYMBOL PADMA GDAN */
+ { BMP_0WIDTH(0x102B, 0x103E) }, /* MYANMAR VOWEL SIGN TALL AA - MYANMAR CONSONANT SIGN MEDIAL HA */
+ { BMP_0WIDTH(0x1056, 0x1059) }, /* MYANMAR VOWEL SIGN VOCALIC R - MYANMAR VOWEL SIGN VOCALIC LL */
+ { BMP_0WIDTH(0x105E, 0x1060) }, /* MYANMAR CONSONANT SIGN MON MEDIAL NA - MYANMAR CONSONANT SIGN MON MEDIAL LA */
+ { BMP_0WIDTH(0x1062, 0x1064) }, /* MYANMAR VOWEL SIGN SGAW KAREN EU - MYANMAR TONE MARK SGAW KAREN KE PHO */
+ { BMP_0WIDTH(0x1067, 0x106D) }, /* MYANMAR VOWEL SIGN WESTERN PWO KAREN EU - MYANMAR SIGN WESTERN PWO KAREN TONE-5 */
+ { BMP_0WIDTH(0x1071, 0x1074) }, /* MYANMAR VOWEL SIGN GEBA KAREN I - MYANMAR VOWEL SIGN KAYAH EE */
+ { BMP_0WIDTH(0x1082, 0x108D) }, /* MYANMAR CONSONANT SIGN SHAN MEDIAL WA - MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE */
+ { BMP_0WIDTH(0x108F, 0x108F) }, /* MYANMAR SIGN RUMAI PALAUNG TONE-5 */
+ { BMP_0WIDTH(0x109A, 0x109D) }, /* MYANMAR SIGN KHAMTI TONE-1 - MYANMAR VOWEL SIGN AITON AI */
+ { BMP_2WIDTH(0x1100, 0x115F) }, /* HANGUL CHOSEONG KIYEOK - HANGUL CHOSEONG FILLER */
+ { BMP_0WIDTH(0x135D, 0x135F) }, /* ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK - ETHIOPIC COMBINING GEMINATION MARK */
+ { BMP_0WIDTH(0x1712, 0x1715) }, /* TAGALOG VOWEL SIGN I - TAGALOG SIGN PAMUDPOD */
+ { BMP_0WIDTH(0x1732, 0x1734) }, /* HANUNOO VOWEL SIGN I - HANUNOO SIGN PAMUDPOD */
+ { BMP_0WIDTH(0x1752, 0x1753) }, /* BUHID VOWEL SIGN I - BUHID VOWEL SIGN U */
+ { BMP_0WIDTH(0x1772, 0x1773) }, /* TAGBANWA VOWEL SIGN I - TAGBANWA VOWEL SIGN U */
+ { BMP_0WIDTH(0x17B4, 0x17D3) }, /* KHMER VOWEL INHERENT AQ - KHMER SIGN BATHAMASAT */
+ { BMP_0WIDTH(0x17DD, 0x17DD) }, /* KHMER SIGN ATTHACAN */
+ { BMP_0WIDTH(0x180B, 0x180F) }, /* MONGOLIAN FREE VARIATION SELECTOR ONE - MONGOLIAN FREE VARIATION SELECTOR FOUR */
+ { BMP_0WIDTH(0x1885, 0x1886) }, /* MONGOLIAN LETTER ALI GALI BALUDA - MONGOLIAN LETTER ALI GALI THREE BALUDA */
+ { BMP_0WIDTH(0x18A9, 0x18A9) }, /* MONGOLIAN LETTER ALI GALI DAGALGA */
+ { BMP_0WIDTH(0x1920, 0x192B) }, /* LIMBU VOWEL SIGN A - LIMBU SUBJOINED LETTER WA */
+ { BMP_0WIDTH(0x1930, 0x193B) }, /* LIMBU SMALL LETTER KA - LIMBU SIGN SA-I */
+ { BMP_0WIDTH(0x1A17, 0x1A1B) }, /* BUGINESE VOWEL SIGN I - BUGINESE VOWEL SIGN AE */
+ { BMP_0WIDTH(0x1A55, 0x1A5E) }, /* TAI THAM CONSONANT SIGN MEDIAL RA - TAI THAM CONSONANT SIGN SA */
+ { BMP_0WIDTH(0x1A60, 0x1A7C) }, /* TAI THAM SIGN SAKOT - TAI THAM SIGN KHUEN-LUE KARAN */
+ { BMP_0WIDTH(0x1A7F, 0x1A7F) }, /* TAI THAM COMBINING CRYPTOGRAMMIC DOT */
+ { BMP_0WIDTH(0x1AB0, 0x1ACE) }, /* COMBINING DOUBLED CIRCUMFLEX ACCENT - COMBINING LATIN SMALL LETTER INSULAR T */
+ { BMP_0WIDTH(0x1B00, 0x1B04) }, /* BALINESE SIGN ULU RICEM - BALINESE SIGN BISAH */
+ { BMP_0WIDTH(0x1B34, 0x1B44) }, /* BALINESE SIGN REREKAN - BALINESE ADEG ADEG */
+ { BMP_0WIDTH(0x1B6B, 0x1B73) }, /* BALINESE MUSICAL SYMBOL COMBINING TEGEH - BALINESE MUSICAL SYMBOL COMBINING GONG */
+ { BMP_0WIDTH(0x1B80, 0x1B82) }, /* SUNDANESE SIGN PANYECEK - SUNDANESE SIGN PANGWISAD */
+ { BMP_0WIDTH(0x1BA1, 0x1BAD) }, /* SUNDANESE CONSONANT SIGN PAMINGKAL - SUNDANESE CONSONANT SIGN PASANGAN WA */
+ { BMP_0WIDTH(0x1BE6, 0x1BF3) }, /* BATAK SIGN TOMPI - BATAK PANONGONAN */
+ { BMP_0WIDTH(0x1C24, 0x1C37) }, /* LEPCHA SUBJOINED LETTER YA - LEPCHA SIGN NUKTA */
+ { BMP_0WIDTH(0x1CD0, 0x1CD2) }, /* VEDIC TONE KARSHANA - VEDIC TONE PRENKHA */
+ { BMP_0WIDTH(0x1CD4, 0x1CE8) }, /* VEDIC SIGN YAJURVEDIC MIDLINE SVARITA - VEDIC SIGN VISARGA ANUDATTA WITH TAIL */
+ { BMP_0WIDTH(0x1CED, 0x1CED) }, /* VEDIC SIGN TIRYAK */
+ { BMP_0WIDTH(0x1CF4, 0x1CF4) }, /* VEDIC TONE CANDRA ABOVE */
+ { BMP_0WIDTH(0x1CF7, 0x1CF9) }, /* VEDIC SIGN ATIKRAMA - VEDIC TONE DOUBLE RING ABOVE */
+ { BMP_0WIDTH(0x1DC0, 0x1DFF) }, /* COMBINING DOTTED GRAVE ACCENT - COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW */
+ { BMP_0WIDTH(0x200B, 0x200F) }, /* ZERO WIDTH SPACE - RIGHT-TO-LEFT MARK */
+ { BMP_0WIDTH(0x202A, 0x202E) }, /* LEFT-TO-RIGHT EMBEDDING - RIGHT-TO-LEFT OVERRIDE */
+ { BMP_0WIDTH(0x2060, 0x2064) }, /* WORD JOINER - INVISIBLE PLUS */
+ { BMP_0WIDTH(0x2066, 0x206F) }, /* LEFT-TO-RIGHT ISOLATE - NOMINAL DIGIT SHAPES */
+ { BMP_0WIDTH(0x20D0, 0x20F0) }, /* COMBINING LEFT HARPOON ABOVE - COMBINING ASTERISK ABOVE */
+ { BMP_2WIDTH(0x231A, 0x231B) }, /* WATCH - HOURGLASS */
+ { BMP_2WIDTH(0x2329, 0x232A) }, /* LEFT-POINTING ANGLE BRACKET - RIGHT-POINTING ANGLE BRACKET */
+ { BMP_2WIDTH(0x23E9, 0x23EC) }, /* BLACK RIGHT-POINTING DOUBLE TRIANGLE - BLACK DOWN-POINTING DOUBLE TRIANGLE */
+ { BMP_2WIDTH(0x23F0, 0x23F0) }, /* ALARM CLOCK */
+ { BMP_2WIDTH(0x23F3, 0x23F3) }, /* HOURGLASS WITH FLOWING SAND */
+ { BMP_2WIDTH(0x25FD, 0x25FE) }, /* WHITE MEDIUM SMALL SQUARE - BLACK MEDIUM SMALL SQUARE */
+ { BMP_2WIDTH(0x2614, 0x2615) }, /* UMBRELLA WITH RAIN DROPS - HOT BEVERAGE */
+ { BMP_2WIDTH(0x2630, 0x2637) }, /* TRIGRAM FOR HEAVEN - TRIGRAM FOR EARTH */
+ { BMP_0WIDTH(0x2640, 0x2640) }, /* FEMALE SIGN */
+ { BMP_0WIDTH(0x2642, 0x2642) }, /* MALE SIGN */
+ { BMP_2WIDTH(0x2648, 0x2653) }, /* ARIES - PISCES */
+ { BMP_2WIDTH(0x267F, 0x267F) }, /* WHEELCHAIR SYMBOL */
+ { BMP_2WIDTH(0x268A, 0x268F) }, /* MONOGRAM FOR YANG - DIGRAM FOR GREATER YIN */
+ { BMP_2WIDTH(0x2693, 0x2693) }, /* ANCHOR */
+ { BMP_2WIDTH(0x26A1, 0x26A1) }, /* HIGH VOLTAGE SIGN */
+ { BMP_0WIDTH(0x26A7, 0x26A7) }, /* MALE WITH STROKE AND MALE AND FEMALE SIGN */
+ { BMP_2WIDTH(0x26AA, 0x26AB) }, /* MEDIUM WHITE CIRCLE - MEDIUM BLACK CIRCLE */
+ { BMP_2WIDTH(0x26BD, 0x26BE) }, /* SOCCER BALL - BASEBALL */
+ { BMP_2WIDTH(0x26C4, 0x26C5) }, /* SNOWMAN WITHOUT SNOW - SUN BEHIND CLOUD */
+ { BMP_2WIDTH(0x26CE, 0x26CE) }, /* OPHIUCHUS */
+ { BMP_2WIDTH(0x26D4, 0x26D4) }, /* NO ENTRY */
+ { BMP_2WIDTH(0x26EA, 0x26EA) }, /* CHURCH */
+ { BMP_2WIDTH(0x26F2, 0x26F3) }, /* FOUNTAIN - FLAG IN HOLE */
+ { BMP_2WIDTH(0x26F5, 0x26F5) }, /* SAILBOAT */
+ { BMP_2WIDTH(0x26FA, 0x26FA) }, /* TENT */
+ { BMP_2WIDTH(0x26FD, 0x26FD) }, /* FUEL PUMP */
+ { BMP_2WIDTH(0x2705, 0x2705) }, /* WHITE HEAVY CHECK MARK */
+ { BMP_2WIDTH(0x270A, 0x270B) }, /* RAISED FIST - RAISED HAND */
+ { BMP_2WIDTH(0x2728, 0x2728) }, /* SPARKLES */
+ { BMP_2WIDTH(0x274C, 0x274C) }, /* CROSS MARK */
+ { BMP_2WIDTH(0x274E, 0x274E) }, /* NEGATIVE SQUARED CROSS MARK */
+ { BMP_2WIDTH(0x2753, 0x2755) }, /* BLACK QUESTION MARK ORNAMENT - WHITE EXCLAMATION MARK ORNAMENT */
+ { BMP_2WIDTH(0x2757, 0x2757) }, /* HEAVY EXCLAMATION MARK SYMBOL */
+ { BMP_2WIDTH(0x2795, 0x2797) }, /* HEAVY PLUS SIGN - HEAVY DIVISION SIGN */
+ { BMP_2WIDTH(0x27B0, 0x27B0) }, /* CURLY LOOP */
+ { BMP_2WIDTH(0x27BF, 0x27BF) }, /* DOUBLE CURLY LOOP */
+ { BMP_2WIDTH(0x2B1B, 0x2B1C) }, /* BLACK LARGE SQUARE - WHITE LARGE SQUARE */
+ { BMP_2WIDTH(0x2B50, 0x2B50) }, /* WHITE MEDIUM STAR */
+ { BMP_2WIDTH(0x2B55, 0x2B55) }, /* HEAVY LARGE CIRCLE */
+ { BMP_0WIDTH(0x2CEF, 0x2CF1) }, /* COPTIC COMBINING NI ABOVE - COPTIC COMBINING SPIRITUS LENIS */
+ { BMP_0WIDTH(0x2D7F, 0x2D7F) }, /* TIFINAGH CONSONANT JOINER */
+ { BMP_0WIDTH(0x2DE0, 0x2DFF) }, /* COMBINING CYRILLIC LETTER BE - COMBINING CYRILLIC LETTER IOTIFIED BIG YUS */
+ { BMP_2WIDTH(0x2E80, 0x2E99) }, /* CJK RADICAL REPEAT - CJK RADICAL RAP */
+ { BMP_2WIDTH(0x2E9B, 0x2EF3) }, /* CJK RADICAL CHOKE - CJK RADICAL C-SIMPLIFIED TURTLE */
+ { BMP_2WIDTH(0x2F00, 0x2FD5) }, /* KANGXI RADICAL ONE - KANGXI RADICAL FLUTE */
+ { BMP_2WIDTH(0x2FF0, 0x3029) }, /* IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT - HANGZHOU NUMERAL NINE */
+ { BMP_0WIDTH(0x302A, 0x302F) }, /* IDEOGRAPHIC LEVEL TONE MARK - HANGUL DOUBLE DOT TONE MARK */
+ { BMP_2WIDTH(0x3030, 0x303E) }, /* WAVY DASH - IDEOGRAPHIC VARIATION INDICATOR */
+ { BMP_2WIDTH(0x3041, 0x3096) }, /* HIRAGANA LETTER SMALL A - HIRAGANA LETTER SMALL KE */
+ { BMP_0WIDTH(0x3099, 0x309A) }, /* COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK - COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
+ { BMP_2WIDTH(0x309B, 0x30FF) }, /* KATAKANA-HIRAGANA VOICED SOUND MARK - KATAKANA DIGRAPH KOTO */
+ { BMP_2WIDTH(0x3105, 0x312F) }, /* BOPOMOFO LETTER B - BOPOMOFO LETTER NN */
+ { BMP_2WIDTH(0x3131, 0x318E) }, /* HANGUL LETTER KIYEOK - HANGUL LETTER ARAEAE */
+ { BMP_2WIDTH(0x3190, 0x31E5) }, /* IDEOGRAPHIC ANNOTATION LINKING MARK - CJK STROKE SZP */
+ { BMP_2WIDTH(0x31EF, 0x321E) }, /* IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION - PARENTHESIZED KOREAN CHARACTER O HU */
+ { BMP_2WIDTH(0x3220, 0x3247) }, /* PARENTHESIZED IDEOGRAPH ONE - CIRCLED IDEOGRAPH KOTO */
+ { BMP_2WIDTH(0x3250, 0xA48C) }, /* PARTNERSHIP SIGN - YI SYLLABLE YYR */
+ { BMP_2WIDTH(0xA490, 0xA4C6) }, /* YI RADICAL QOT - YI RADICAL KE */
+ { BMP_0WIDTH(0xA66F, 0xA672) }, /* COMBINING CYRILLIC VZMET - COMBINING CYRILLIC THOUSAND MILLIONS SIGN */
+ { BMP_0WIDTH(0xA674, 0xA67D) }, /* COMBINING CYRILLIC LETTER UKRAINIAN IE - COMBINING CYRILLIC PAYEROK */
+ { BMP_0WIDTH(0xA69E, 0xA69F) }, /* COMBINING CYRILLIC LETTER EF - COMBINING CYRILLIC LETTER IOTIFIED E */
+ { BMP_0WIDTH(0xA6F0, 0xA6F1) }, /* BAMUM COMBINING MARK KOQNDON - BAMUM COMBINING MARK TUKWENTIS */
+ { BMP_0WIDTH(0xA802, 0xA802) }, /* SYLOTI NAGRI SIGN DVISVARA */
+ { BMP_0WIDTH(0xA806, 0xA806) }, /* SYLOTI NAGRI SIGN HASANTA */
+ { BMP_0WIDTH(0xA80B, 0xA80B) }, /* SYLOTI NAGRI SIGN ANUSVARA */
+ { BMP_0WIDTH(0xA823, 0xA827) }, /* SYLOTI NAGRI VOWEL SIGN A - SYLOTI NAGRI VOWEL SIGN OO */
+ { BMP_0WIDTH(0xA82C, 0xA82C) }, /* SYLOTI NAGRI SIGN ALTERNATE HASANTA */
+ { BMP_0WIDTH(0xA880, 0xA881) }, /* SAURASHTRA SIGN ANUSVARA - SAURASHTRA SIGN VISARGA */
+ { BMP_0WIDTH(0xA8B4, 0xA8C5) }, /* SAURASHTRA CONSONANT SIGN HAARU - SAURASHTRA SIGN CANDRABINDU */
+ { BMP_0WIDTH(0xA8E0, 0xA8F1) }, /* COMBINING DEVANAGARI DIGIT ZERO - COMBINING DEVANAGARI SIGN AVAGRAHA */
+ { BMP_0WIDTH(0xA8FF, 0xA8FF) }, /* DEVANAGARI VOWEL SIGN AY */
+ { BMP_0WIDTH(0xA926, 0xA92D) }, /* KAYAH LI VOWEL UE - KAYAH LI TONE CALYA PLOPHU */
+ { BMP_0WIDTH(0xA947, 0xA953) }, /* REJANG VOWEL SIGN I - REJANG VIRAMA */
+ { BMP_2WIDTH(0xA960, 0xA97C) }, /* HANGUL CHOSEONG TIKEUT-MIEUM - HANGUL CHOSEONG SSANGYEORINHIEUH */
+ { BMP_0WIDTH(0xA980, 0xA983) }, /* JAVANESE SIGN PANYANGGA - JAVANESE SIGN WIGNYAN */
+ { BMP_0WIDTH(0xA9B3, 0xA9C0) }, /* JAVANESE SIGN CECAK TELU - JAVANESE PANGKON */
+ { BMP_0WIDTH(0xA9E5, 0xA9E5) }, /* MYANMAR SIGN SHAN SAW */
+ { BMP_0WIDTH(0xAA29, 0xAA36) }, /* CHAM VOWEL SIGN AA - CHAM CONSONANT SIGN WA */
+ { BMP_0WIDTH(0xAA43, 0xAA43) }, /* CHAM CONSONANT SIGN FINAL NG */
+ { BMP_0WIDTH(0xAA4C, 0xAA4D) }, /* CHAM CONSONANT SIGN FINAL M - CHAM CONSONANT SIGN FINAL H */
+ { BMP_0WIDTH(0xAA7B, 0xAA7D) }, /* MYANMAR SIGN PAO KAREN TONE - MYANMAR SIGN TAI LAING TONE-5 */
+ { BMP_0WIDTH(0xAAB0, 0xAAB0) }, /* TAI VIET MAI KANG */
+ { BMP_0WIDTH(0xAAB2, 0xAAB4) }, /* TAI VIET VOWEL I - TAI VIET VOWEL U */
+ { BMP_0WIDTH(0xAAB7, 0xAAB8) }, /* TAI VIET MAI KHIT - TAI VIET VOWEL IA */
+ { BMP_0WIDTH(0xAABE, 0xAABF) }, /* TAI VIET VOWEL AM - TAI VIET TONE MAI EK */
+ { BMP_0WIDTH(0xAAC1, 0xAAC1) }, /* TAI VIET TONE MAI THO */
+ { BMP_0WIDTH(0xAAEB, 0xAAEF) }, /* MEETEI MAYEK VOWEL SIGN II - MEETEI MAYEK VOWEL SIGN AAU */
+ { BMP_0WIDTH(0xAAF5, 0xAAF6) }, /* MEETEI MAYEK VOWEL SIGN VISARGA - MEETEI MAYEK VIRAMA */
+ { BMP_0WIDTH(0xABE3, 0xABEA) }, /* MEETEI MAYEK VOWEL SIGN ONAP - MEETEI MAYEK VOWEL SIGN NUNG */
+ { BMP_0WIDTH(0xABEC, 0xABED) }, /* MEETEI MAYEK LUM IYEK - MEETEI MAYEK APUN IYEK */
+ { BMP_2WIDTH(0xAC00, 0xD7A3) }, /* HANGUL SYLLABLE GA - HANGUL SYLLABLE HIH */
+ { BMP_2WIDTH(0xF900, 0xFAFF) }, /* U+F900 - U+FAFF */
+ { BMP_0WIDTH(0xFB1E, 0xFB1E) }, /* HEBREW POINT JUDEO-SPANISH VARIKA */
+ { BMP_0WIDTH(0xFE00, 0xFE0F) }, /* VARIATION SELECTOR-1 - VARIATION SELECTOR-16 */
+ { BMP_2WIDTH(0xFE10, 0xFE19) }, /* PRESENTATION FORM FOR VERTICAL COMMA - PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS */
+ { BMP_0WIDTH(0xFE20, 0xFE2F) }, /* COMBINING LIGATURE LEFT HALF - COMBINING CYRILLIC TITLO RIGHT HALF */
+ { BMP_2WIDTH(0xFE30, 0xFE52) }, /* PRESENTATION FORM FOR VERTICAL TWO DOT LEADER - SMALL FULL STOP */
+ { BMP_2WIDTH(0xFE54, 0xFE66) }, /* SMALL SEMICOLON - SMALL EQUALS SIGN */
+ { BMP_2WIDTH(0xFE68, 0xFE6B) }, /* SMALL REVERSE SOLIDUS - SMALL COMMERCIAL AT */
+ { BMP_0WIDTH(0xFEFF, 0xFEFF) }, /* ZERO WIDTH NO-BREAK SPACE */
+ { BMP_2WIDTH(0xFF01, 0xFF60) }, /* FULLWIDTH EXCLAMATION MARK - FULLWIDTH RIGHT WHITE PARENTHESIS */
+ { BMP_2WIDTH(0xFFE0, 0xFFE6) }, /* FULLWIDTH CENT SIGN - FULLWIDTH WON SIGN */
+ { BMP_0WIDTH(0xFFF9, 0xFFFB) }, /* INTERLINEAR ANNOTATION ANCHOR - INTERLINEAR ANNOTATION TERMINATOR */
+};
+
+/* Combined zero- and double-width ranges (non-BMP, U+10000 and above).
+ * The first 33 entries host the BMP double-width bitmap in the low
+ * 8 bits of `last`. */
+static const struct ucs_width32 ucs_nonbmp_ranges[] = {
+ { RANGE_0WIDTH(0x101FD, 0x101FD) /* PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 0.. 7] */
+ { RANGE_0WIDTH(0x102E0, 0x102E0) /* COPTIC EPACT THOUSANDS MARK */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 8.. 15] */
+ { RANGE_0WIDTH(0x10376, 0x1037A) /* COMBINING OLD PERMIC LETTER AN - COMBINING OLD PERMIC LETTER SII */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 16.. 23] */
+ { RANGE_0WIDTH(0x10A01, 0x10A03) /* KHAROSHTHI VOWEL SIGN I - KHAROSHTHI VOWEL SIGN VOCALIC R */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 24.. 31] */
+ { RANGE_0WIDTH(0x10A05, 0x10A06) /* KHAROSHTHI VOWEL SIGN E - KHAROSHTHI VOWEL SIGN O */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 32.. 39] */
+ { RANGE_0WIDTH(0x10A0C, 0x10A0F) /* KHAROSHTHI VOWEL LENGTH MARK - KHAROSHTHI SIGN VISARGA */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 40.. 47] */
+ { RANGE_0WIDTH(0x10A38, 0x10A3A) /* KHAROSHTHI SIGN BAR ABOVE - KHAROSHTHI SIGN DOT BELOW */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 48.. 55] */
+ { RANGE_0WIDTH(0x10A3F, 0x10A3F) /* KHAROSHTHI VIRAMA */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 56.. 63] */
+ { RANGE_0WIDTH(0x10AE5, 0x10AE6) /* MANICHAEAN ABBREVIATION MARK ABOVE - MANICHAEAN ABBREVIATION MARK BELOW */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 64.. 71] */
+ { RANGE_0WIDTH(0x10D24, 0x10D27) /* HANIFI ROHINGYA SIGN HARBAHAY - HANIFI ROHINGYA SIGN TASSI */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 72.. 79] */
+ { RANGE_0WIDTH(0x10D69, 0x10D6D) /* GARAY VOWEL SIGN E - GARAY CONSONANT NASALIZATION MARK */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 80.. 87] */
+ { RANGE_0WIDTH(0x10EAB, 0x10EAC) /* YEZIDI COMBINING HAMZA MARK - YEZIDI COMBINING MADDA MARK */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 88.. 95] */
+ { RANGE_0WIDTH(0x10EFC, 0x10EFF) /* ARABIC COMBINING ALEF OVERLAY - ARABIC SMALL LOW WORD MADDA */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [ 96..103] */
+ { RANGE_0WIDTH(0x10F46, 0x10F50) /* SOGDIAN COMBINING DOT BELOW - SOGDIAN COMBINING STROKE BELOW */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [104..111] */
+ { RANGE_0WIDTH(0x10F82, 0x10F85) /* OLD UYGHUR COMBINING DOT ABOVE - OLD UYGHUR COMBINING TWO DOTS BELOW */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [112..119] */
+ { RANGE_0WIDTH(0x11000, 0x11002) /* BRAHMI SIGN CANDRABINDU - BRAHMI SIGN VISARGA */
+ | BMP_2W_BITS(0b00001000) }, /* BMP entries [120..127] */
+ { RANGE_0WIDTH(0x11038, 0x11046) /* BRAHMI VOWEL SIGN AA - BRAHMI VIRAMA */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [128..135] */
+ { RANGE_0WIDTH(0x11070, 0x11070) /* BRAHMI SIGN OLD TAMIL VIRAMA */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [136..143] */
+ { RANGE_0WIDTH(0x11073, 0x11074) /* BRAHMI VOWEL SIGN OLD TAMIL SHORT E - BRAHMI VOWEL SIGN OLD TAMIL SHORT O */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [144..151] */
+ { RANGE_0WIDTH(0x1107F, 0x11082) /* BRAHMI NUMBER JOINER - KAITHI SIGN VISARGA */
+ | BMP_2W_BITS(0b10000000) }, /* BMP entries [152..159] */
+ { RANGE_0WIDTH(0x110B0, 0x110BA) /* KAITHI VOWEL SIGN AA - KAITHI SIGN NUKTA */
+ | BMP_2W_BITS(0b01111111) }, /* BMP entries [160..167] */
+ { RANGE_0WIDTH(0x110BD, 0x110BD) /* KAITHI NUMBER SIGN */
+ | BMP_2W_BITS(0b10111110) }, /* BMP entries [168..175] */
+ { RANGE_0WIDTH(0x110C2, 0x110C2) /* KAITHI VOWEL SIGN VOCALIC R */
+ | BMP_2W_BITS(0b11111111) }, /* BMP entries [176..183] */
+ { RANGE_0WIDTH(0x110CD, 0x110CD) /* KAITHI NUMBER SIGN ABOVE */
+ | BMP_2W_BITS(0b11111111) }, /* BMP entries [184..191] */
+ { RANGE_0WIDTH(0x11100, 0x11102) /* CHAKMA SIGN CANDRABINDU - CHAKMA SIGN VISARGA */
+ | BMP_2W_BITS(0b00111111) }, /* BMP entries [192..199] */
+ { RANGE_0WIDTH(0x11127, 0x11134) /* CHAKMA VOWEL SIGN A - CHAKMA MAAYYAA */
+ | BMP_2W_BITS(0b11011110) }, /* BMP entries [200..207] */
+ { RANGE_0WIDTH(0x11145, 0x11146) /* CHAKMA VOWEL SIGN AA - CHAKMA VOWEL SIGN EI */
+ | BMP_2W_BITS(0b11111110) }, /* BMP entries [208..215] */
+ { RANGE_0WIDTH(0x11173, 0x11173) /* MAHAJANI SIGN NUKTA */
+ | BMP_2W_BITS(0b00000001) }, /* BMP entries [216..223] */
+ { RANGE_0WIDTH(0x11180, 0x11182) /* SHARADA SIGN CANDRABINDU - SHARADA SIGN VISARGA */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [224..231] */
+ { RANGE_0WIDTH(0x111B3, 0x111C0) /* SHARADA VOWEL SIGN AA - SHARADA SIGN VIRAMA */
+ | BMP_2W_BITS(0b00000001) }, /* BMP entries [232..239] */
+ { RANGE_0WIDTH(0x111C9, 0x111CC) /* SHARADA SANDHI MARK - SHARADA EXTRA SHORT VOWEL MARK */
+ | BMP_2W_BITS(0b00000000) }, /* BMP entries [240..247] */
+ { RANGE_0WIDTH(0x111CE, 0x111CF) /* SHARADA VOWEL SIGN PRISHTHAMATRA E - SHARADA SIGN INVERTED CANDRABINDU */
+ | BMP_2W_BITS(0b10100110) }, /* BMP entries [248..255] */
+ { RANGE_0WIDTH(0x1122C, 0x11237) /* KHOJKI VOWEL SIGN AA - KHOJKI SIGN SHADDA */
+ | BMP_2W_BITS(0b00011011) }, /* BMP entries [256..261] */
+ { RANGE_0WIDTH(0x1123E, 0x1123E) }, /* KHOJKI SIGN SUKUN */
+ { RANGE_0WIDTH(0x11241, 0x11241) }, /* KHOJKI VOWEL SIGN VOCALIC R */
+ { RANGE_0WIDTH(0x112DF, 0x112EA) }, /* KHUDAWADI SIGN ANUSVARA - KHUDAWADI SIGN VIRAMA */
+ { RANGE_0WIDTH(0x11300, 0x11303) }, /* GRANTHA SIGN COMBINING ANUSVARA ABOVE - GRANTHA SIGN VISARGA */
+ { RANGE_0WIDTH(0x1133B, 0x1133C) }, /* COMBINING BINDU BELOW - GRANTHA SIGN NUKTA */
+ { RANGE_0WIDTH(0x1133E, 0x11344) }, /* GRANTHA VOWEL SIGN AA - GRANTHA VOWEL SIGN VOCALIC RR */
+ { RANGE_0WIDTH(0x11347, 0x11348) }, /* GRANTHA VOWEL SIGN EE - GRANTHA VOWEL SIGN AI */
+ { RANGE_0WIDTH(0x1134B, 0x1134D) }, /* GRANTHA VOWEL SIGN OO - GRANTHA SIGN VIRAMA */
+ { RANGE_0WIDTH(0x11357, 0x11357) }, /* GRANTHA AU LENGTH MARK */
+ { RANGE_0WIDTH(0x11362, 0x11363) }, /* GRANTHA VOWEL SIGN VOCALIC L - GRANTHA VOWEL SIGN VOCALIC LL */
+ { RANGE_0WIDTH(0x11366, 0x1136C) }, /* COMBINING GRANTHA DIGIT ZERO - COMBINING GRANTHA DIGIT SIX */
+ { RANGE_0WIDTH(0x11370, 0x11374) }, /* COMBINING GRANTHA LETTER A - COMBINING GRANTHA LETTER PA */
+ { RANGE_0WIDTH(0x113B8, 0x113C0) }, /* TULU-TIGALARI VOWEL SIGN AA - TULU-TIGALARI VOWEL SIGN VOCALIC LL */
+ { RANGE_0WIDTH(0x113C2, 0x113C2) }, /* TULU-TIGALARI VOWEL SIGN EE */
+ { RANGE_0WIDTH(0x113C5, 0x113C5) }, /* TULU-TIGALARI VOWEL SIGN AI */
+ { RANGE_0WIDTH(0x113C7, 0x113CA) }, /* TULU-TIGALARI VOWEL SIGN OO - TULU-TIGALARI SIGN CANDRA ANUNASIKA */
+ { RANGE_0WIDTH(0x113CC, 0x113D0) }, /* TULU-TIGALARI SIGN ANUSVARA - TULU-TIGALARI CONJOINER */
+ { RANGE_0WIDTH(0x113D2, 0x113D2) }, /* TULU-TIGALARI GEMINATION MARK */
+ { RANGE_0WIDTH(0x113E1, 0x113E2) }, /* TULU-TIGALARI VEDIC TONE SVARITA - TULU-TIGALARI VEDIC TONE ANUDATTA */
+ { RANGE_0WIDTH(0x11435, 0x11446) }, /* NEWA VOWEL SIGN AA - NEWA SIGN NUKTA */
+ { RANGE_0WIDTH(0x1145E, 0x1145E) }, /* NEWA SANDHI MARK */
+ { RANGE_0WIDTH(0x114B0, 0x114C3) }, /* TIRHUTA VOWEL SIGN AA - TIRHUTA SIGN NUKTA */
+ { RANGE_0WIDTH(0x115AF, 0x115B5) }, /* SIDDHAM VOWEL SIGN AA - SIDDHAM VOWEL SIGN VOCALIC RR */
+ { RANGE_0WIDTH(0x115B8, 0x115C0) }, /* SIDDHAM VOWEL SIGN E - SIDDHAM SIGN NUKTA */
+ { RANGE_0WIDTH(0x115DC, 0x115DD) }, /* SIDDHAM VOWEL SIGN ALTERNATE U - SIDDHAM VOWEL SIGN ALTERNATE UU */
+ { RANGE_0WIDTH(0x11630, 0x11640) }, /* MODI VOWEL SIGN AA - MODI SIGN ARDHACANDRA */
+ { RANGE_0WIDTH(0x116AB, 0x116B7) }, /* TAKRI SIGN ANUSVARA - TAKRI SIGN NUKTA */
+ { RANGE_0WIDTH(0x1171D, 0x1172B) }, /* AHOM CONSONANT SIGN MEDIAL LA - AHOM SIGN KILLER */
+ { RANGE_0WIDTH(0x1182C, 0x1183A) }, /* DOGRA VOWEL SIGN AA - DOGRA SIGN NUKTA */
+ { RANGE_0WIDTH(0x11930, 0x11935) }, /* DIVES AKURU VOWEL SIGN AA - DIVES AKURU VOWEL SIGN E */
+ { RANGE_0WIDTH(0x11937, 0x11938) }, /* DIVES AKURU VOWEL SIGN AI - DIVES AKURU VOWEL SIGN O */
+ { RANGE_0WIDTH(0x1193B, 0x1193E) }, /* DIVES AKURU SIGN ANUSVARA - DIVES AKURU VIRAMA */
+ { RANGE_0WIDTH(0x11940, 0x11940) }, /* DIVES AKURU MEDIAL YA */
+ { RANGE_0WIDTH(0x11942, 0x11943) }, /* DIVES AKURU MEDIAL RA - DIVES AKURU SIGN NUKTA */
+ { RANGE_0WIDTH(0x119D1, 0x119D7) }, /* NANDINAGARI VOWEL SIGN AA - NANDINAGARI VOWEL SIGN VOCALIC RR */
+ { RANGE_0WIDTH(0x119DA, 0x119E0) }, /* NANDINAGARI VOWEL SIGN E - NANDINAGARI SIGN VIRAMA */
+ { RANGE_0WIDTH(0x119E4, 0x119E4) }, /* NANDINAGARI VOWEL SIGN PRISHTHAMATRA E */
+ { RANGE_0WIDTH(0x11A01, 0x11A0A) }, /* ZANABAZAR SQUARE VOWEL SIGN I - ZANABAZAR SQUARE VOWEL LENGTH MARK */
+ { RANGE_0WIDTH(0x11A33, 0x11A39) }, /* ZANABAZAR SQUARE FINAL CONSONANT MARK - ZANABAZAR SQUARE SIGN VISARGA */
+ { RANGE_0WIDTH(0x11A3B, 0x11A3E) }, /* ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA - ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA */
+ { RANGE_0WIDTH(0x11A47, 0x11A47) }, /* ZANABAZAR SQUARE SUBJOINER */
+ { RANGE_0WIDTH(0x11A51, 0x11A5B) }, /* SOYOMBO VOWEL SIGN I - SOYOMBO VOWEL LENGTH MARK */
+ { RANGE_0WIDTH(0x11A8A, 0x11A99) }, /* SOYOMBO FINAL CONSONANT SIGN G - SOYOMBO SUBJOINER */
+ { RANGE_0WIDTH(0x11C2F, 0x11C36) }, /* BHAIKSUKI VOWEL SIGN AA - BHAIKSUKI VOWEL SIGN VOCALIC L */
+ { RANGE_0WIDTH(0x11C38, 0x11C3F) }, /* BHAIKSUKI VOWEL SIGN E - BHAIKSUKI SIGN VIRAMA */
+ { RANGE_0WIDTH(0x11C92, 0x11CA7) }, /* MARCHEN SUBJOINED LETTER KA - MARCHEN SUBJOINED LETTER ZA */
+ { RANGE_0WIDTH(0x11CA9, 0x11CB6) }, /* MARCHEN SUBJOINED LETTER YA - MARCHEN SIGN CANDRABINDU */
+ { RANGE_0WIDTH(0x11D31, 0x11D36) }, /* MASARAM GONDI VOWEL SIGN AA - MASARAM GONDI VOWEL SIGN VOCALIC R */
+ { RANGE_0WIDTH(0x11D3A, 0x11D3A) }, /* MASARAM GONDI VOWEL SIGN E */
+ { RANGE_0WIDTH(0x11D3C, 0x11D3D) }, /* MASARAM GONDI VOWEL SIGN AI - MASARAM GONDI VOWEL SIGN O */
+ { RANGE_0WIDTH(0x11D3F, 0x11D45) }, /* MASARAM GONDI VOWEL SIGN AU - MASARAM GONDI VIRAMA */
+ { RANGE_0WIDTH(0x11D47, 0x11D47) }, /* MASARAM GONDI RA-KARA */
+ { RANGE_0WIDTH(0x11D8A, 0x11D8E) }, /* GUNJALA GONDI VOWEL SIGN AA - GUNJALA GONDI VOWEL SIGN UU */
+ { RANGE_0WIDTH(0x11D90, 0x11D91) }, /* GUNJALA GONDI VOWEL SIGN EE - GUNJALA GONDI VOWEL SIGN AI */
+ { RANGE_0WIDTH(0x11D93, 0x11D97) }, /* GUNJALA GONDI VOWEL SIGN OO - GUNJALA GONDI VIRAMA */
+ { RANGE_0WIDTH(0x11EF3, 0x11EF6) }, /* MAKASAR VOWEL SIGN I - MAKASAR VOWEL SIGN O */
+ { RANGE_0WIDTH(0x11F00, 0x11F01) }, /* KAWI SIGN CANDRABINDU - KAWI SIGN ANUSVARA */
+ { RANGE_0WIDTH(0x11F03, 0x11F03) }, /* KAWI SIGN VISARGA */
+ { RANGE_0WIDTH(0x11F34, 0x11F3A) }, /* KAWI VOWEL SIGN AA - KAWI VOWEL SIGN VOCALIC R */
+ { RANGE_0WIDTH(0x11F3E, 0x11F42) }, /* KAWI VOWEL SIGN E - KAWI CONJOINER */
+ { RANGE_0WIDTH(0x11F5A, 0x11F5A) }, /* KAWI SIGN NUKTA */
+ { RANGE_0WIDTH(0x13430, 0x13440) }, /* EGYPTIAN HIEROGLYPH VERTICAL JOINER - EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY */
+ { RANGE_0WIDTH(0x13447, 0x13455) }, /* EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START - EGYPTIAN HIEROGLYPH MODIFIER DAMAGED */
+ { RANGE_0WIDTH(0x1611E, 0x1612F) }, /* GURUNG KHEMA VOWEL SIGN AA - GURUNG KHEMA SIGN THOLHOMA */
+ { RANGE_0WIDTH(0x16AF0, 0x16AF4) }, /* BASSA VAH COMBINING HIGH TONE - BASSA VAH COMBINING HIGH-LOW TONE */
+ { RANGE_0WIDTH(0x16B30, 0x16B36) }, /* PAHAWH HMONG MARK CIM TUB - PAHAWH HMONG MARK CIM TAUM */
+ { RANGE_0WIDTH(0x16F4F, 0x16F4F) }, /* MIAO SIGN CONSONANT MODIFIER BAR */
+ { RANGE_0WIDTH(0x16F51, 0x16F87) }, /* MIAO SIGN ASPIRATION - MIAO VOWEL SIGN UI */
+ { RANGE_0WIDTH(0x16F8F, 0x16F92) }, /* MIAO TONE RIGHT - MIAO TONE BELOW */
+ { RANGE_2WIDTH(0x16FE0, 0x16FE3) }, /* TANGUT ITERATION MARK - OLD CHINESE ITERATION MARK */
+ { RANGE_0WIDTH(0x16FE4, 0x16FE4) }, /* KHITAN SMALL SCRIPT FILLER */
+ { RANGE_0WIDTH(0x16FF0, 0x16FF1) }, /* VIETNAMESE ALTERNATE READING MARK CA - VIETNAMESE ALTERNATE READING MARK NHAY */
+ { RANGE_2WIDTH(0x17000, 0x187F7) }, /* U+17000 - U+187F7 */
+ { RANGE_2WIDTH(0x18800, 0x18CD5) }, /* TANGUT COMPONENT-001 - KHITAN SMALL SCRIPT CHARACTER-18CD5 */
+ { RANGE_2WIDTH(0x18CFF, 0x18D08) }, /* U+18CFF - U+18D08 */
+ { RANGE_2WIDTH(0x1AFF0, 0x1AFF3) }, /* KATAKANA LETTER MINNAN TONE-2 - KATAKANA LETTER MINNAN TONE-5 */
+ { RANGE_2WIDTH(0x1AFF5, 0x1AFFB) }, /* KATAKANA LETTER MINNAN TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-5 */
+ { RANGE_2WIDTH(0x1AFFD, 0x1AFFE) }, /* KATAKANA LETTER MINNAN NASALIZED TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-8 */
+ { RANGE_2WIDTH(0x1B000, 0x1B122) }, /* KATAKANA LETTER ARCHAIC E - KATAKANA LETTER ARCHAIC WU */
+ { RANGE_2WIDTH(0x1B132, 0x1B132) }, /* HIRAGANA LETTER SMALL KO */
+ { RANGE_2WIDTH(0x1B150, 0x1B152) }, /* HIRAGANA LETTER SMALL WI - HIRAGANA LETTER SMALL WO */
+ { RANGE_2WIDTH(0x1B155, 0x1B155) }, /* KATAKANA LETTER SMALL KO */
+ { RANGE_2WIDTH(0x1B164, 0x1B167) }, /* KATAKANA LETTER SMALL WI - KATAKANA LETTER SMALL N */
+ { RANGE_2WIDTH(0x1B170, 0x1B2FB) }, /* NUSHU CHARACTER-1B170 - NUSHU CHARACTER-1B2FB */
+ { RANGE_0WIDTH(0x1BC9D, 0x1BC9E) }, /* DUPLOYAN THICK LETTER SELECTOR - DUPLOYAN DOUBLE MARK */
+ { RANGE_0WIDTH(0x1BCA0, 0x1BCA3) }, /* SHORTHAND FORMAT LETTER OVERLAP - SHORTHAND FORMAT UP STEP */
+ { RANGE_0WIDTH(0x1CF00, 0x1CF2D) }, /* ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT - ZNAMENNY COMBINING MARK KRYZH ON LEFT */
+ { RANGE_0WIDTH(0x1CF30, 0x1CF46) }, /* ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO - ZNAMENNY PRIZNAK MODIFIER ROG */
+ { RANGE_0WIDTH(0x1D165, 0x1D169) }, /* MUSICAL SYMBOL COMBINING STEM - MUSICAL SYMBOL COMBINING TREMOLO-3 */
+ { RANGE_0WIDTH(0x1D16D, 0x1D182) }, /* MUSICAL SYMBOL COMBINING AUGMENTATION DOT - MUSICAL SYMBOL COMBINING LOURE */
+ { RANGE_0WIDTH(0x1D185, 0x1D18B) }, /* MUSICAL SYMBOL COMBINING DOIT - MUSICAL SYMBOL COMBINING TRIPLE TONGUE */
+ { RANGE_0WIDTH(0x1D1AA, 0x1D1AD) }, /* MUSICAL SYMBOL COMBINING DOWN BOW - MUSICAL SYMBOL COMBINING SNAP PIZZICATO */
+ { RANGE_0WIDTH(0x1D242, 0x1D244) }, /* COMBINING GREEK MUSICAL TRISEME - COMBINING GREEK MUSICAL PENTASEME */
+ { RANGE_2WIDTH(0x1D300, 0x1D356) }, /* MONOGRAM FOR EARTH - TETRAGRAM FOR FOSTERING */
+ { RANGE_2WIDTH(0x1D360, 0x1D376) }, /* COUNTING ROD UNIT DIGIT ONE - IDEOGRAPHIC TALLY MARK FIVE */
+ { RANGE_0WIDTH(0x1DA00, 0x1DA36) }, /* SIGNWRITING HEAD RIM - SIGNWRITING AIR SUCKING IN */
+ { RANGE_0WIDTH(0x1DA3B, 0x1DA6C) }, /* SIGNWRITING MOUTH CLOSED NEUTRAL - SIGNWRITING EXCITEMENT */
+ { RANGE_0WIDTH(0x1DA75, 0x1DA75) }, /* SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS */
+ { RANGE_0WIDTH(0x1DA84, 0x1DA84) }, /* SIGNWRITING LOCATION HEAD NECK */
+ { RANGE_0WIDTH(0x1DA9B, 0x1DA9F) }, /* SIGNWRITING FILL MODIFIER-2 - SIGNWRITING FILL MODIFIER-6 */
+ { RANGE_0WIDTH(0x1DAA1, 0x1DAAF) }, /* SIGNWRITING ROTATION MODIFIER-2 - SIGNWRITING ROTATION MODIFIER-16 */
+ { RANGE_0WIDTH(0x1E000, 0x1E006) }, /* COMBINING GLAGOLITIC LETTER AZU - COMBINING GLAGOLITIC LETTER ZHIVETE */
+ { RANGE_0WIDTH(0x1E008, 0x1E018) }, /* COMBINING GLAGOLITIC LETTER ZEMLJA - COMBINING GLAGOLITIC LETTER HERU */
+ { RANGE_0WIDTH(0x1E01B, 0x1E021) }, /* COMBINING GLAGOLITIC LETTER SHTA - COMBINING GLAGOLITIC LETTER YATI */
+ { RANGE_0WIDTH(0x1E023, 0x1E024) }, /* COMBINING GLAGOLITIC LETTER YU - COMBINING GLAGOLITIC LETTER SMALL YUS */
+ { RANGE_0WIDTH(0x1E026, 0x1E02A) }, /* COMBINING GLAGOLITIC LETTER YO - COMBINING GLAGOLITIC LETTER FITA */
+ { RANGE_0WIDTH(0x1E08F, 0x1E08F) }, /* COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
+ { RANGE_0WIDTH(0x1E130, 0x1E136) }, /* NYIAKENG PUACHUE HMONG TONE-B - NYIAKENG PUACHUE HMONG TONE-D */
+ { RANGE_0WIDTH(0x1E2AE, 0x1E2AE) }, /* TOTO SIGN RISING TONE */
+ { RANGE_0WIDTH(0x1E2EC, 0x1E2EF) }, /* WANCHO TONE TUP - WANCHO TONE KOINI */
+ { RANGE_0WIDTH(0x1E4EC, 0x1E4EF) }, /* NAG MUNDARI SIGN MUHOR - NAG MUNDARI SIGN SUTUH */
+ { RANGE_0WIDTH(0x1E5EE, 0x1E5EF) }, /* OL ONAL SIGN MU - OL ONAL SIGN IKIR */
+ { RANGE_0WIDTH(0x1E8D0, 0x1E8D6) }, /* MENDE KIKAKUI COMBINING NUMBER TEENS - MENDE KIKAKUI COMBINING NUMBER MILLIONS */
+ { RANGE_0WIDTH(0x1E944, 0x1E94A) }, /* ADLAM ALIF LENGTHENER - ADLAM NUKTA */
+ { RANGE_2WIDTH(0x1F000, 0x1F02F) }, /* U+1F000 - U+1F02F */
+ { RANGE_2WIDTH(0x1F0A0, 0x1F0FF) }, /* U+1F0A0 - U+1F0FF */
+ { RANGE_2WIDTH(0x1F18E, 0x1F18E) }, /* NEGATIVE SQUARED AB */
+ { RANGE_2WIDTH(0x1F191, 0x1F19A) }, /* SQUARED CL - SQUARED VS */
+ { RANGE_2WIDTH(0x1F200, 0x1F202) }, /* SQUARE HIRAGANA HOKA - SQUARED KATAKANA SA */
+ { RANGE_2WIDTH(0x1F210, 0x1F23B) }, /* SQUARED CJK UNIFIED IDEOGRAPH-624B - SQUARED CJK UNIFIED IDEOGRAPH-914D */
+ { RANGE_2WIDTH(0x1F240, 0x1F248) }, /* TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C - TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 */
+ { RANGE_2WIDTH(0x1F250, 0x1F251) }, /* CIRCLED IDEOGRAPH ADVANTAGE - CIRCLED IDEOGRAPH ACCEPT */
+ { RANGE_2WIDTH(0x1F260, 0x1F265) }, /* ROUNDED SYMBOL FOR FU - ROUNDED SYMBOL FOR CAI */
+ { RANGE_2WIDTH(0x1F300, 0x1F3FA) }, /* CYCLONE - AMPHORA */
+ { RANGE_0WIDTH(0x1F3FB, 0x1F3FF) }, /* EMOJI MODIFIER FITZPATRICK TYPE-1-2 - EMOJI MODIFIER FITZPATRICK TYPE-6 */
+ { RANGE_2WIDTH(0x1F400, 0x1F64F) }, /* RAT - PERSON WITH FOLDED HANDS */
+ { RANGE_2WIDTH(0x1F680, 0x1F9AF) }, /* ROCKET - PROBING CANE */
+ { RANGE_0WIDTH(0x1F9B0, 0x1F9B3) }, /* EMOJI COMPONENT RED HAIR - EMOJI COMPONENT WHITE HAIR */
+ { RANGE_2WIDTH(0x1F9B4, 0x1FAFF) }, /* U+1F9B4 - U+1FAFF */
+ { RANGE_2WIDTH(0x20000, 0x2FFFD) }, /* U+20000 - U+2FFFD */
+ { RANGE_2WIDTH(0x30000, 0x3FFFD) }, /* U+30000 - U+3FFFD */
+ { RANGE_0WIDTH(0xE0001, 0xE0001) }, /* LANGUAGE TAG */
+ { RANGE_0WIDTH(0xE0020, 0xE007F) }, /* TAG SPACE - CANCEL TAG */
+ { RANGE_0WIDTH(0xE0100, 0xE01EF) }, /* VARIATION SELECTOR-17 - VARIATION SELECTOR-256 */
+};
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
new file mode 100644
index 000000000..bf1502fd5
--- /dev/null
+++ b/drivers/tty/vt/vc_screen.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Provide access to virtual console memory.
+ * /dev/vcs: 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]
+ *
+ * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values
+ * instead of 1-byte screen glyph values.
+ * [minor: N+64]
+ *
+ * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented).
+ *
+ * This replaces screendump and part of selection, so that the system
+ * administrator can control access using file system permissions.
+ *
+ * aeb@cwi.nl - efter Friedas begravelse - 950211
+ *
+ * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
+ * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
+ * - making it shorter - scr_readw are macros which expand in PRETTY long code
+ */
+
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/kbd_kern.h>
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/unaligned.h>
+
+#define HEADER_SIZE 4u
+#define CON_BUF_SIZE (IS_ENABLED(CONFIG_BASE_SMALL) ? 256 : PAGE_SIZE)
+
+/*
+ * Our minor space:
+ *
+ * 0 ... 63 glyph mode without attributes
+ * 64 ... 127 unicode mode without attributes
+ * 128 ... 191 glyph mode with attributes
+ * 192 ... 255 unused (reserved for unicode with attributes)
+ *
+ * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles
+ * with minors 0, 64, 128 and 192 being proxies for the foreground console.
+ */
+#if MAX_NR_CONSOLES > 63
+#warning "/dev/vcs* devices may not accommodate more than 63 consoles"
+#endif
+
+#define console(inode) (iminor(inode) & 63)
+#define use_unicode(inode) (iminor(inode) & 64)
+#define use_attributes(inode) (iminor(inode) & 128)
+
+struct vcs_poll_data {
+ struct notifier_block notifier;
+ unsigned int cons_num;
+ int event;
+ 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;
+ int fa_band;
+
+ switch (code) {
+ case VT_UPDATE:
+ fa_band = POLL_PRI;
+ break;
+ case VT_DEALLOCATE:
+ fa_band = POLL_HUP;
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ if (currcons == 0)
+ currcons = fg_console;
+ else
+ currcons--;
+ if (currcons != vc->vc_num)
+ return NOTIFY_DONE;
+
+ poll->event = code;
+ wake_up_interruptible(&poll->waitq);
+ kill_fasync(&poll->fasync, SIGIO, fa_band);
+ 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, *kill = NULL;
+
+ if (poll)
+ return poll;
+
+ poll = kzalloc_obj(*poll);
+ if (!poll)
+ return NULL;
+ poll->cons_num = console(file_inode(file));
+ init_waitqueue_head(&poll->waitq);
+ poll->notifier.notifier_call = vcs_notifier;
+ /*
+ * In order not to lose any update event, we must pretend one might
+ * have occurred before we have a chance to register our notifier.
+ * This is also how user space has come to detect which kernels
+ * support POLLPRI on /dev/vcs* devices i.e. using poll() with
+ * POLLPRI and a zero timeout.
+ */
+ poll->event = VT_UPDATE;
+
+ 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 */
+ kill = poll;
+ poll = file->private_data;
+ }
+ spin_unlock(&file->f_lock);
+ if (kill)
+ vcs_poll_data_free(kill);
+
+ return poll;
+}
+
+/**
+ * vcs_vc - return VC for @inode
+ * @inode: inode for which to return a VC
+ * @viewed: returns whether this console is currently foreground (viewed)
+ *
+ * Must be called with console_lock.
+ */
+static struct vc_data *vcs_vc(struct inode *inode, bool *viewed)
+{
+ unsigned int currcons = console(inode);
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (currcons == 0) {
+ currcons = fg_console;
+ if (viewed)
+ *viewed = true;
+ } else {
+ currcons--;
+ if (viewed)
+ *viewed = false;
+ }
+ return vc_cons[currcons].d;
+}
+
+/**
+ * vcs_size - return size for a VC in @vc
+ * @vc: which VC
+ * @attr: does it use attributes?
+ * @unicode: is it unicode?
+ *
+ * Must be called with console_lock.
+ */
+static int vcs_size(const struct vc_data *vc, bool attr, bool unicode)
+{
+ int size;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ size = vc->vc_rows * vc->vc_cols;
+
+ if (attr) {
+ if (unicode)
+ return -EOPNOTSUPP;
+
+ size = 2 * size + HEADER_SIZE;
+ } else if (unicode)
+ size *= 4;
+
+ return size;
+}
+
+static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
+{
+ struct inode *inode = file_inode(file);
+ struct vc_data *vc;
+ int size;
+
+ scoped_guard(console_lock) {
+ vc = vcs_vc(inode, NULL);
+ if (!vc)
+ return -ENXIO;
+
+ size = vcs_size(vc, use_attributes(inode), use_unicode(inode));
+ }
+ if (size < 0)
+ return size;
+ return fixed_size_llseek(file, offset, orig, size);
+}
+
+static int vcs_read_buf_uni(struct vc_data *vc, char *con_buf,
+ unsigned int pos, unsigned int count, bool viewed)
+{
+ unsigned int nr, row, col, maxcol = vc->vc_cols;
+ int ret;
+
+ ret = vc_uniscr_check(vc);
+ if (ret)
+ return ret;
+
+ pos /= 4;
+ row = pos / maxcol;
+ col = pos % maxcol;
+ nr = maxcol - col;
+ do {
+ if (nr > count / 4)
+ nr = count / 4;
+ vc_uniscr_copy_line(vc, con_buf, viewed, row, col, nr);
+ con_buf += nr * 4;
+ count -= nr * 4;
+ row++;
+ col = 0;
+ nr = maxcol;
+ } while (count);
+
+ return 0;
+}
+
+static void vcs_read_buf_noattr(const struct vc_data *vc, char *con_buf,
+ unsigned int pos, unsigned int count, bool viewed)
+{
+ u16 *org;
+ unsigned int col, maxcol = vc->vc_cols;
+
+ org = screen_pos(vc, pos, viewed);
+ col = pos % maxcol;
+ pos += maxcol - col;
+
+ while (count-- > 0) {
+ *con_buf++ = (vcs_scr_readw(vc, org++) & 0xff);
+ if (++col == maxcol) {
+ org = screen_pos(vc, pos, viewed);
+ col = 0;
+ pos += maxcol;
+ }
+ }
+}
+
+static unsigned int vcs_read_buf(const struct vc_data *vc, char *con_buf,
+ unsigned int pos, unsigned int count, bool viewed,
+ unsigned int *skip)
+{
+ u16 *org, *con_buf16;
+ unsigned int col, maxcol = vc->vc_cols;
+ unsigned int filled = count;
+
+ if (pos < HEADER_SIZE) {
+ /* clamp header values if they don't fit */
+ con_buf[0] = min(vc->vc_rows, 0xFFu);
+ con_buf[1] = min(vc->vc_cols, 0xFFu);
+ getconsxy(vc, con_buf + 2);
+
+ *skip += pos;
+ count += pos;
+ if (count > CON_BUF_SIZE) {
+ count = CON_BUF_SIZE;
+ filled = count - pos;
+ }
+
+ /* Advance state pointers and move on. */
+ count -= min(HEADER_SIZE, count);
+ pos = HEADER_SIZE;
+ con_buf += HEADER_SIZE;
+ /* If count >= 0, then pos is even... */
+ } else if (pos & 1) {
+ /*
+ * Skip first byte for output if start address is odd. Update
+ * region sizes up/down depending on free space in buffer.
+ */
+ (*skip)++;
+ if (count < CON_BUF_SIZE)
+ count++;
+ else
+ filled--;
+ }
+
+ if (!count)
+ return filled;
+
+ pos -= HEADER_SIZE;
+ pos /= 2;
+ col = pos % maxcol;
+
+ org = screen_pos(vc, pos, viewed);
+ pos += maxcol - col;
+
+ /*
+ * Buffer has even length, so we can always copy character + attribute.
+ * We do not copy last byte to userspace if count is odd.
+ */
+ count = (count + 1) / 2;
+ con_buf16 = (u16 *)con_buf;
+
+ while (count) {
+ *con_buf16++ = vcs_scr_readw(vc, org++);
+ count--;
+ if (++col == maxcol) {
+ org = screen_pos(vc, pos, viewed);
+ col = 0;
+ pos += maxcol;
+ }
+ }
+
+ return filled;
+}
+
+static ssize_t
+vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = file_inode(file);
+ struct vc_data *vc;
+ struct vcs_poll_data *poll;
+ unsigned int read;
+ ssize_t ret;
+ loff_t pos;
+ bool viewed, attr, uni_mode;
+
+ char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!con_buf)
+ return -ENOMEM;
+
+ pos = *ppos;
+
+ /* Select the proper current console and verify
+ * sanity of the situation under the console lock.
+ */
+ guard(console_lock)();
+
+ uni_mode = use_unicode(inode);
+ attr = use_attributes(inode);
+
+ if (pos < 0)
+ return -EINVAL;
+ /* we enforce 32-bit alignment for pos and count in unicode mode */
+ if (uni_mode && (pos | count) & 3)
+ return -EINVAL;
+
+ poll = file->private_data;
+ if (count && poll)
+ poll->event = 0;
+ read = 0;
+ ret = 0;
+ while (count) {
+ unsigned int this_round, skip = 0;
+ int size;
+
+ vc = vcs_vc(inode, &viewed);
+ if (!vc) {
+ ret = -ENXIO;
+ break;
+ }
+
+ /* Check whether we are above size each round,
+ * as copy_to_user at the end of this loop
+ * could sleep.
+ */
+ size = vcs_size(vc, attr, uni_mode);
+ if (size < 0) {
+ ret = size;
+ break;
+ }
+ 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.
+ */
+
+ if (uni_mode) {
+ ret = vcs_read_buf_uni(vc, con_buf, pos, this_round,
+ viewed);
+ if (ret)
+ break;
+ } else if (!attr) {
+ vcs_read_buf_noattr(vc, con_buf, pos, this_round,
+ viewed);
+ } else {
+ this_round = vcs_read_buf(vc, con_buf, pos, this_round,
+ viewed, &skip);
+ }
+
+ /* 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().
+ */
+
+ console_unlock();
+ ret = copy_to_user(buf, con_buf + skip, this_round);
+ console_lock();
+
+ if (ret) {
+ read += this_round - ret;
+ ret = -EFAULT;
+ break;
+ }
+ buf += this_round;
+ pos += this_round;
+ read += this_round;
+ count -= this_round;
+ }
+ *ppos += read;
+ if (read)
+ return read;
+
+ return ret;
+}
+
+static u16 *vcs_write_buf_noattr(struct vc_data *vc, const char *con_buf,
+ unsigned int pos, unsigned int count, bool viewed, u16 **org0)
+{
+ u16 *org;
+ unsigned int col, maxcol = vc->vc_cols;
+
+ *org0 = org = screen_pos(vc, pos, viewed);
+ col = pos % maxcol;
+ pos += maxcol - col;
+
+ while (count > 0) {
+ unsigned char c = *con_buf++;
+
+ count--;
+ vcs_scr_writew(vc,
+ (vcs_scr_readw(vc, org) & 0xff00) | c, org);
+ org++;
+ if (++col == maxcol) {
+ org = screen_pos(vc, pos, viewed);
+ col = 0;
+ pos += maxcol;
+ }
+ }
+
+ return org;
+}
+
+/*
+ * Compilers (gcc 10) are unable to optimize the swap in cpu_to_le16. So do it
+ * the poor man way.
+ */
+static inline u16 vc_compile_le16(u8 hi, u8 lo)
+{
+#ifdef __BIG_ENDIAN
+ return (lo << 8u) | hi;
+#else
+ return (hi << 8u) | lo;
+#endif
+}
+
+static u16 *vcs_write_buf(struct vc_data *vc, const char *con_buf,
+ unsigned int pos, unsigned int count, bool viewed, u16 **org0)
+{
+ u16 *org;
+ unsigned int col, maxcol = vc->vc_cols;
+ unsigned char c;
+
+ /* header */
+ if (pos < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+
+ getconsxy(vc, header + 2);
+ while (pos < HEADER_SIZE && count > 0) {
+ count--;
+ header[pos++] = *con_buf++;
+ }
+ if (!viewed)
+ putconsxy(vc, header + 2);
+ }
+
+ if (!count)
+ return NULL;
+
+ pos -= HEADER_SIZE;
+ col = (pos/2) % maxcol;
+
+ *org0 = org = screen_pos(vc, pos/2, viewed);
+
+ /* odd pos -- the first single character */
+ if (pos & 1) {
+ count--;
+ c = *con_buf++;
+ vcs_scr_writew(vc, vc_compile_le16(c, vcs_scr_readw(vc, org)),
+ org);
+ org++;
+ pos++;
+ if (++col == maxcol) {
+ org = screen_pos(vc, pos/2, viewed);
+ col = 0;
+ }
+ }
+
+ pos /= 2;
+ pos += maxcol - col;
+
+ /* even pos -- handle attr+character pairs */
+ while (count > 1) {
+ unsigned short w;
+
+ w = get_unaligned(((unsigned short *)con_buf));
+ vcs_scr_writew(vc, w, org++);
+ con_buf += 2;
+ count -= 2;
+ if (++col == maxcol) {
+ org = screen_pos(vc, pos, viewed);
+ col = 0;
+ pos += maxcol;
+ }
+ }
+
+ if (!count)
+ return org;
+
+ /* odd pos -- the remaining character */
+ c = *con_buf++;
+ vcs_scr_writew(vc, vc_compile_le16(vcs_scr_readw(vc, org) >> 8, c),
+ org);
+
+ return org;
+}
+
+static ssize_t
+vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = file_inode(file);
+ struct vc_data *vc;
+ u16 *org0, *org;
+ unsigned int written;
+ int size;
+ ssize_t ret;
+ loff_t pos;
+ bool viewed, attr;
+
+ if (use_unicode(inode))
+ return -EOPNOTSUPP;
+
+ char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!con_buf)
+ return -ENOMEM;
+
+ pos = *ppos;
+
+ /* Select the proper current console and verify
+ * sanity of the situation under the console lock.
+ */
+ guard(console_lock)();
+
+ attr = use_attributes(inode);
+ vc = vcs_vc(inode, &viewed);
+ if (!vc)
+ return -ENXIO;
+
+ size = vcs_size(vc, attr, false);
+ if (size < 0)
+ return size;
+ if (pos < 0 || pos > size)
+ return -EINVAL;
+ if (count > size - pos)
+ count = size - pos;
+ written = 0;
+ while (count) {
+ unsigned int this_round = count;
+
+ 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.
+ */
+ console_unlock();
+ ret = copy_from_user(con_buf, buf, this_round);
+ console_lock();
+
+ if (ret) {
+ this_round -= ret;
+ if (!this_round) {
+ /* Abort loop if no data were copied. Otherwise
+ * fail with -EFAULT.
+ */
+ if (written)
+ break;
+ return -EFAULT;
+ }
+ }
+
+ /* The vc might have been freed or vcs_size might have changed
+ * while we slept to grab the user buffer, so recheck.
+ * Return data written up to now on failure.
+ */
+ vc = vcs_vc(inode, &viewed);
+ if (!vc) {
+ if (written)
+ break;
+ return -ENXIO;
+ }
+ size = vcs_size(vc, attr, false);
+ if (size < 0) {
+ if (written)
+ break;
+ return size;
+ }
+ 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.
+ */
+
+ if (attr)
+ org = vcs_write_buf(vc, con_buf, pos, this_round,
+ viewed, &org0);
+ else
+ org = vcs_write_buf_noattr(vc, con_buf, pos, this_round,
+ viewed, &org0);
+
+ count -= this_round;
+ written += this_round;
+ buf += this_round;
+ pos += this_round;
+ if (org)
+ update_region(vc, (unsigned long)(org0), org - org0);
+ }
+ *ppos += written;
+ ret = written;
+ if (written && vc)
+ vcs_scr_updated(vc);
+
+ return ret;
+}
+
+static __poll_t
+vcs_poll(struct file *file, poll_table *wait)
+{
+ struct vcs_poll_data *poll = vcs_poll_data_get(file);
+ __poll_t ret = DEFAULT_POLLMASK|EPOLLERR;
+
+ if (poll) {
+ poll_wait(file, &poll->waitq, wait);
+ switch (poll->event) {
+ case VT_UPDATE:
+ ret = DEFAULT_POLLMASK|EPOLLPRI;
+ break;
+ case VT_DEALLOCATE:
+ ret = DEFAULT_POLLMASK|EPOLLHUP|EPOLLERR;
+ break;
+ case 0:
+ ret = DEFAULT_POLLMASK;
+ break;
+ }
+ }
+ 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 = console(inode);
+ bool attr = use_attributes(inode);
+ bool uni_mode = use_unicode(inode);
+
+ /* we currently don't support attributes in unicode mode */
+ if (attr && uni_mode)
+ return -EOPNOTSUPP;
+
+ guard(console_lock)();
+
+ if (currcons && !vc_cons_allocated(currcons - 1))
+ return -ENXIO;
+
+ return 0;
+}
+
+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 const struct class vc_class = {
+ .name = "vc",
+};
+
+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 + 65), NULL, "vcsu%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 + 65));
+ 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);
+ if (class_register(&vc_class))
+ panic("unable to create vc_class");
+
+ device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
+ device_create(&vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
+ 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 000000000..57edf3749
--- /dev/null
+++ b/drivers/tty/vt/vt.c
@@ -0,0 +1,5113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * Hopefully this will be a rather complete VT102 implementation.
+ *
+ * Beeping thanks to John T Kohl.
+ *
+ * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
+ * Chars, and VT100 enhancements by Peter MacDonald.
+ *
+ * Copy and paste function by Andrew Haylett,
+ * some enhancements by Alessandro Rubini.
+ *
+ * Code to check for different video-cards mostly by Galen Hunt,
+ * <g-hunt@ee.utah.edu>
+ *
+ * Rudimentary ISO 10646/Unicode/UTF-8 character set support by
+ * Markus Kuhn, <mskuhn@immd4.informatik.uni-erlangen.de>.
+ *
+ * Dynamic allocation of consoles, aeb@cwi.nl, May 1994
+ * Resizing of consoles, aeb, 940926
+ *
+ * Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94
+ * <poe@daimi.aau.dk>
+ *
+ * User-defined bell sound, new setterm control sequences and printk
+ * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95
+ *
+ * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp>
+ *
+ * Merge with the abstract console driver by Geert Uytterhoeven
+ * <geert@linux-m68k.org>, Jan 1997.
+ *
+ * Original m68k console driver modifications by
+ *
+ * - Arno Griffioen <arno@usn.nl>
+ * - David Carter <carter@cs.bris.ac.uk>
+ *
+ * The abstract console driver provides a generic interface for a text
+ * console. It supports VGA text mode, frame buffer based graphical consoles
+ * and special graphics processors that are only accessible through some
+ * registers (e.g. a TMS340x0 GSP).
+ *
+ * The interface to the hardware is specified using a special structure
+ * (struct consw) which contains function pointers to console operations
+ * (see <linux/console.h> for more information).
+ *
+ * Support for changeable cursor shape
+ * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997
+ *
+ * Ported to i386 and con_scrolldelta fixed
+ * by Emmanuel Marty <core@ggi-project.org>, April 1998
+ *
+ * Resurrected character buffers in videoram plus lots of other trickery
+ * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998
+ *
+ * Removed old-style timers, introduced console_timer, made timer
+ * deletion SMP-safe. 17Jun00, Andrew Morton
+ *
+ * Removed console_lock, enabled interrupts across all console operations
+ * 13 March 2001, Andrew Morton
+ *
+ * Fixed UTF-8 mode so alternate charset modes always work according
+ * to control sequences interpreted in do_con_trol function
+ * preserving backward VT100 semigraphics compatibility,
+ * malformed UTF sequences represented as sequences of replacement glyphs,
+ * original codes or '?' as a last resort if replacement glyph is undefined
+ * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched/signal.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/hex.h>
+#include <linux/kd.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/tiocl.h>
+#include <linux/kbd_kern.h>
+#include <linux/consolemap.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/font.h>
+#include <linux/bitops.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/kdb.h>
+#include <linux/ctype.h>
+#include <linux/gcd.h>
+
+#define MAX_NR_CON_DRIVER 16
+
+#define CON_DRIVER_FLAG_MODULE 1
+#define CON_DRIVER_FLAG_INIT 2
+#define CON_DRIVER_FLAG_ATTR 4
+#define CON_DRIVER_FLAG_ZOMBIE 8
+
+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;
+
+/*
+ * Here is the default bell parameters: 750HZ, 1/8th of a second
+ */
+#define DEFAULT_BELL_PITCH 750
+#define DEFAULT_BELL_DURATION (HZ/8)
+#define DEFAULT_CURSOR_BLINK_MS 200
+
+struct vc vc_cons [MAX_NR_CONSOLES];
+EXPORT_SYMBOL(vc_cons);
+
+static const struct consw *con_driver_map[MAX_NR_CONSOLES];
+
+static int con_open(struct tty_struct *, struct file *);
+static void vc_init(struct vc_data *vc, int do_clear);
+static void gotoxy(struct vc_data *vc, int new_x, int new_y);
+static void restore_cur(struct vc_data *vc);
+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(u8 __user *mode);
+static void set_cursor(struct vc_data *vc);
+static void hide_cursor(struct vc_data *vc);
+static void console_callback(struct work_struct *ignored);
+static void con_driver_unregister_callback(struct work_struct *ignored);
+static void blank_screen_t(struct timer_list *unused);
+static void set_palette(struct vc_data *vc);
+static void unblank_screen(void);
+
+#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)
+
+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);
+EXPORT_SYMBOL(global_cursor_default);
+
+static int cur_default = CUR_UNDERLINE;
+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;
+EXPORT_SYMBOL(console_blanked);
+
+static enum vesa_blank_mode vesa_blank_mode;
+static int vesa_off_interval;
+static int blankinterval;
+core_param(consoleblank, blankinterval, int, 0444);
+
+static DECLARE_WORK(console_work, console_callback);
+static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_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,
+ */
+int fg_console;
+EXPORT_SYMBOL(fg_console);
+int last_console;
+int want_console = -1;
+
+/*
+ * 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);
+EXPORT_SYMBOL(console_blank_hook);
+
+static DEFINE_TIMER(console_timer, blank_screen_t);
+static int blank_state;
+static int blank_timer_expired;
+enum {
+ blank_off = 0,
+ blank_normal_wait,
+ blank_vesa_wait,
+};
+
+/*
+ * struct vc_font
+ */
+
+/**
+ * vc_font_pitch - Calculates the number of bytes between two adjacent scanlines
+ * @font: The VC font
+ *
+ * Returns:
+ * The number of bytes between two adjacent scanlines in the font data
+ */
+unsigned int vc_font_pitch(const struct vc_font *font)
+{
+ return font_glyph_pitch(font->width);
+}
+EXPORT_SYMBOL_GPL(vc_font_pitch);
+
+/**
+ * vc_font_size - Calculates the size of the font data in bytes
+ * @font: The VC font
+ *
+ * vc_font_size() calculates the number of bytes of font data in the
+ * font specified by @font. The function calculates the size from the
+ * font parameters.
+ *
+ * Returns:
+ * The size of the font data in bytes.
+ */
+unsigned int vc_font_size(const struct vc_font *font)
+{
+ return font_glyph_size(font->width, font->height) * font->charcount;
+}
+EXPORT_SYMBOL_GPL(vc_font_size);
+
+/*
+ * /sys/class/tty/tty0/
+ *
+ * the attribute 'active' contains the name of the current vc
+ * console and it supports poll() to detect vc switches
+ */
+static struct device *tty0dev;
+
+/*
+ * 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, .c = unicode };
+ atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, &param);
+}
+
+static void notify_update(struct vc_data *vc)
+{
+ struct vt_notifier_param param = { .vc = vc };
+ atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, &param);
+}
+/*
+ * Low-Level Functions
+ */
+
+static inline bool con_is_fg(const struct vc_data *vc)
+{
+ return vc->vc_num == fg_console;
+}
+
+static inline bool con_should_update(const struct vc_data *vc)
+{
+ return con_is_visible(vc) && !console_blanked;
+}
+
+static inline u16 *screenpos(const struct vc_data *vc, unsigned int offset,
+ bool viewed)
+{
+ unsigned long origin = viewed ? vc->vc_visible_origin : vc->vc_origin;
+
+ return (u16 *)(origin + offset);
+}
+
+static void con_putc(struct vc_data *vc, u16 ca, unsigned int y, unsigned int x)
+{
+ if (vc->vc_sw->con_putc)
+ vc->vc_sw->con_putc(vc, ca, y, x);
+ else
+ vc->vc_sw->con_putcs(vc, &ca, 1, y, x);
+}
+
+/* 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);
+}
+
+/*
+ * Code to manage unicode-based screen buffers
+ */
+
+/*
+ * Our screen buffer is preceded by an array of line pointers so that
+ * scrolling only implies some pointer shuffling.
+ */
+
+static u32 **vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+{
+ u32 **uni_lines;
+ void *p;
+ unsigned int memsize, i, col_size = cols * sizeof(**uni_lines);
+
+ /* allocate everything in one go */
+ memsize = col_size * rows;
+ memsize += rows * sizeof(*uni_lines);
+ uni_lines = vzalloc(memsize);
+ if (!uni_lines)
+ return NULL;
+
+ /* initial line pointers */
+ p = uni_lines + rows;
+ for (i = 0; i < rows; i++) {
+ uni_lines[i] = p;
+ p += col_size;
+ }
+
+ return uni_lines;
+}
+
+static void vc_uniscr_free(u32 **uni_lines)
+{
+ vfree(uni_lines);
+}
+
+static void vc_uniscr_set(struct vc_data *vc, u32 **new_uni_lines)
+{
+ vc_uniscr_free(vc->vc_uni_lines);
+ vc->vc_uni_lines = new_uni_lines;
+}
+
+static void vc_uniscr_putc(struct vc_data *vc, u32 uc)
+{
+ if (vc->vc_uni_lines)
+ vc->vc_uni_lines[vc->state.y][vc->state.x] = uc;
+}
+
+static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
+{
+ if (vc->vc_uni_lines) {
+ u32 *ln = vc->vc_uni_lines[vc->state.y];
+ unsigned int x = vc->state.x, cols = vc->vc_cols;
+
+ memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+ memset32(&ln[x], ' ', nr);
+ }
+}
+
+static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
+{
+ if (vc->vc_uni_lines) {
+ u32 *ln = vc->vc_uni_lines[vc->state.y];
+ unsigned int x = vc->state.x, cols = vc->vc_cols;
+
+ memmove(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+ memset32(&ln[cols - nr], ' ', nr);
+ }
+}
+
+static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
+ unsigned int nr)
+{
+ if (vc->vc_uni_lines)
+ memset32(&vc->vc_uni_lines[vc->state.y][x], ' ', nr);
+}
+
+static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
+ unsigned int nr)
+{
+ if (vc->vc_uni_lines)
+ while (nr--)
+ memset32(vc->vc_uni_lines[y++], ' ', vc->vc_cols);
+}
+
+/* juggling array rotation algorithm (complexity O(N), size complexity O(1)) */
+static void juggle_array(u32 **array, unsigned int size, unsigned int nr)
+{
+ unsigned int gcd_idx;
+
+ for (gcd_idx = 0; gcd_idx < gcd(nr, size); gcd_idx++) {
+ u32 *gcd_idx_val = array[gcd_idx];
+ unsigned int dst_idx = gcd_idx;
+
+ while (1) {
+ unsigned int src_idx = (dst_idx + nr) % size;
+ if (src_idx == gcd_idx)
+ break;
+
+ array[dst_idx] = array[src_idx];
+ dst_idx = src_idx;
+ }
+
+ array[dst_idx] = gcd_idx_val;
+ }
+}
+
+static void vc_uniscr_scroll(struct vc_data *vc, unsigned int top,
+ unsigned int bottom, enum con_scroll dir,
+ unsigned int nr)
+{
+ u32 **uni_lines = vc->vc_uni_lines;
+ unsigned int size = bottom - top;
+
+ if (!uni_lines)
+ return;
+
+ if (dir == SM_DOWN) {
+ juggle_array(&uni_lines[top], size, size - nr);
+ vc_uniscr_clear_lines(vc, top, nr);
+ } else {
+ juggle_array(&uni_lines[top], size, nr);
+ vc_uniscr_clear_lines(vc, bottom - nr, nr);
+ }
+}
+
+static u32 vc_uniscr_getc(struct vc_data *vc, int relative_pos)
+{
+ int pos = vc->state.x + vc->vc_need_wrap + relative_pos;
+
+ if (vc->vc_uni_lines && in_range(pos, 0, vc->vc_cols))
+ return vc->vc_uni_lines[vc->state.y][pos];
+ return 0;
+}
+
+static void vc_uniscr_copy_area(u32 **dst_lines,
+ unsigned int dst_cols,
+ unsigned int dst_rows,
+ u32 **src_lines,
+ unsigned int src_cols,
+ unsigned int src_top_row,
+ unsigned int src_bot_row)
+{
+ unsigned int dst_row = 0;
+
+ if (!dst_lines)
+ return;
+
+ while (src_top_row < src_bot_row) {
+ u32 *src_line = src_lines[src_top_row];
+ u32 *dst_line = dst_lines[dst_row];
+
+ memcpy(dst_line, src_line, src_cols * sizeof(*src_line));
+ if (dst_cols - src_cols)
+ memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
+ src_top_row++;
+ dst_row++;
+ }
+ while (dst_row < dst_rows) {
+ u32 *dst_line = dst_lines[dst_row];
+
+ memset32(dst_line, ' ', dst_cols);
+ dst_row++;
+ }
+}
+
+/*
+ * Called from vcs_read() to make sure unicode screen retrieval is possible.
+ * This will initialize the unicode screen buffer if not already done.
+ * This returns 0 if OK, or a negative error code otherwise.
+ * In particular, -ENODATA is returned if the console is not in UTF-8 mode.
+ */
+int vc_uniscr_check(struct vc_data *vc)
+{
+ u32 **uni_lines;
+ unsigned short *p;
+ int x, y, mask;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (!vc->vc_utf)
+ return -ENODATA;
+
+ if (vc->vc_uni_lines)
+ return 0;
+
+ uni_lines = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+ if (!uni_lines)
+ return -ENOMEM;
+
+ /*
+ * Let's populate it initially with (imperfect) reverse translation.
+ * This is the next best thing we can do short of having it enabled
+ * from the start even when no users rely on this functionality. True
+ * unicode content will be available after a complete screen refresh.
+ */
+ p = (unsigned short *)vc->vc_origin;
+ mask = vc->vc_hi_font_mask | 0xff;
+ for (y = 0; y < vc->vc_rows; y++) {
+ u32 *line = uni_lines[y];
+ for (x = 0; x < vc->vc_cols; x++) {
+ u16 glyph = scr_readw(p++) & mask;
+ line[x] = inverse_translate(vc, glyph, true);
+ }
+ }
+
+ vc->vc_uni_lines = uni_lines;
+
+ return 0;
+}
+
+/*
+ * Called from vcs_read() to get the unicode data from the screen.
+ * This must be preceded by a successful call to vc_uniscr_check() once
+ * the console lock has been taken.
+ */
+void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
+ unsigned int row, unsigned int col, unsigned int nr)
+{
+ u32 **uni_lines = vc->vc_uni_lines;
+ int offset = row * vc->vc_size_row + col * 2;
+ unsigned long pos;
+
+ if (WARN_ON_ONCE(!uni_lines))
+ return;
+
+ pos = (unsigned long)screenpos(vc, offset, viewed);
+ if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
+ /*
+ * Desired position falls in the main screen buffer.
+ * However the actual row/col might be different if
+ * scrollback is active.
+ */
+ row = (pos - vc->vc_origin) / vc->vc_size_row;
+ col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
+ memcpy(dest, &uni_lines[row][col], nr * sizeof(u32));
+ } else {
+ /*
+ * Scrollback is active. For now let's simply backtranslate
+ * the screen glyphs until the unicode screen buffer does
+ * synchronize with console display drivers for a scrollback
+ * buffer of its own.
+ */
+ u16 *p = (u16 *)pos;
+ int mask = vc->vc_hi_font_mask | 0xff;
+ u32 *uni_buf = dest;
+ while (nr--) {
+ u16 glyph = scr_readw(p++) & mask;
+ *uni_buf++ = inverse_translate(vc, glyph, true);
+ }
+ }
+}
+
+static void con_scroll(struct vc_data *vc, unsigned int top,
+ unsigned int bottom, enum con_scroll dir,
+ unsigned int nr)
+{
+ unsigned int rows = bottom - top;
+ u16 *clear, *dst, *src;
+
+ if (top + nr >= bottom)
+ nr = rows - 1;
+ if (bottom > vc->vc_rows || top >= bottom || nr < 1)
+ return;
+
+ vc_uniscr_scroll(vc, top, bottom, dir, nr);
+ if (con_is_visible(vc) &&
+ vc->vc_sw->con_scroll(vc, top, bottom, dir, nr))
+ return;
+
+ src = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * top);
+ dst = (u16 *)(vc->vc_origin + vc->vc_size_row * (top + nr));
+
+ if (dir == SM_UP) {
+ clear = src + (rows - nr) * vc->vc_cols;
+ swap(src, dst);
+ }
+ scr_memmovew(dst, src, (rows - nr) * vc->vc_size_row);
+ scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
+}
+
+static void do_update_region(struct vc_data *vc, unsigned long start, int count)
+{
+ unsigned int xx, yy, offset;
+ u16 *p = (u16 *)start;
+
+ offset = (start - vc->vc_origin) / 2;
+ xx = offset % vc->vc_cols;
+ yy = offset / vc->vc_cols;
+
+ 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++;
+ }
+}
+
+void update_region(struct vc_data *vc, unsigned long start, int count)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (con_should_update(vc)) {
+ hide_cursor(vc);
+ do_update_region(vc, start, count);
+ set_cursor(vc);
+ }
+}
+EXPORT_SYMBOL(update_region);
+
+/* Structure of attributes is hardware-dependent */
+
+static u8 build_attr(struct vc_data *vc, u8 _color,
+ enum vc_intensity _intensity, bool _blink, bool _underline,
+ bool _reverse, bool _italic)
+{
+ if (vc->vc_sw->con_build_attr)
+ return vc->vc_sw->con_build_attr(vc, _color, _intensity,
+ _blink, _underline, _reverse, _italic);
+
+/*
+ * ++roman: I completely changed the attribute format for monochrome
+ * mode (!can_do_color). The formerly used MDA (monochrome display
+ * adapter) format didn't allow the combination of certain effects.
+ * Now the attribute is just a bit vector:
+ * Bit 0..1: intensity (0..2)
+ * Bit 2 : underline
+ * Bit 3 : reverse
+ * Bit 7 : blink
+ */
+ {
+ u8 a = _color;
+ if (!vc->vc_can_do_color)
+ return _intensity |
+ (_italic << 1) |
+ (_underline << 2) |
+ (_reverse << 3) |
+ (_blink << 7);
+ if (_italic)
+ a = (a & 0xF0) | vc->vc_itcolor;
+ else if (_underline)
+ a = (a & 0xf0) | vc->vc_ulcolor;
+ else if (_intensity == VCI_HALF_BRIGHT)
+ a = (a & 0xf0) | vc->vc_halfcolor;
+ if (_reverse)
+ a = (a & 0x88) | (((a >> 4) | (a << 4)) & 0x77);
+ if (_blink)
+ a ^= 0x80;
+ if (_intensity == VCI_BOLD)
+ a ^= 0x08;
+ if (vc->vc_hi_font_mask == 0x100)
+ a <<= 1;
+ return a;
+ }
+}
+
+static void update_attr(struct vc_data *vc)
+{
+ vc->vc_attr = build_attr(vc, vc->state.color, vc->state.intensity,
+ vc->state.blink, vc->state.underline,
+ vc->state.reverse ^ vc->vc_decscnm, vc->state.italic);
+ vc->vc_video_erase_char = ' ' | (build_attr(vc, vc->state.color,
+ VCI_NORMAL, vc->state.blink, false,
+ vc->vc_decscnm, false) << 8);
+}
+
+/* Note: inverting the screen twice should revert to the original state */
+void invert_screen(struct vc_data *vc, int offset, int count, bool viewed)
+{
+ u16 *p;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ count /= 2;
+ p = screenpos(vc, offset, viewed);
+ if (vc->vc_sw->con_invert_region) {
+ vc->vc_sw->con_invert_region(vc, p, count);
+ } else {
+ u16 *q = p;
+ int cnt = count;
+ u16 a;
+
+ if (!vc->vc_can_do_color) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a ^= 0x0800;
+ scr_writew(a, q);
+ q++;
+ }
+ } else if (vc->vc_hi_font_mask == 0x100) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x11ff) |
+ ((a & 0xe000) >> 4) |
+ ((a & 0x0e00) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ } else {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x88ff) |
+ ((a & 0x7000) >> 4) |
+ ((a & 0x0700) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ }
+ }
+
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) p, count);
+ notify_update(vc);
+}
+
+/* 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, true));
+ if (con_should_update(vc))
+ con_putc(vc, old, oldy, oldx);
+ notify_update(vc);
+ }
+
+ old_offset = offset;
+
+ if (offset != -1 && offset >= 0 &&
+ offset < vc->vc_screenbuf_size) {
+ unsigned short new;
+ u16 *p = screenpos(vc, offset, true);
+ old = scr_readw(p);
+ new = old ^ vc->vc_complement_mask;
+ scr_writew(new, p);
+ if (con_should_update(vc)) {
+ oldx = (offset >> 1) % vc->vc_cols;
+ oldy = (offset >> 1) / vc->vc_cols;
+ con_putc(vc, new, oldy, oldx);
+ }
+ notify_update(vc);
+ }
+}
+
+static void insert_char(struct vc_data *vc, unsigned int nr)
+{
+ unsigned short *p = (unsigned short *) vc->vc_pos;
+
+ vc_uniscr_insert(vc, nr);
+ scr_memmovew(p + nr, p, (vc->vc_cols - vc->state.x - nr) * 2);
+ scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
+ vc->vc_need_wrap = 0;
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) p,
+ vc->vc_cols - vc->state.x);
+}
+
+static void delete_char(struct vc_data *vc, unsigned int nr)
+{
+ unsigned short *p = (unsigned short *) vc->vc_pos;
+
+ vc_uniscr_delete(vc, nr);
+ scr_memmovew(p, p + nr, (vc->vc_cols - vc->state.x - nr) * 2);
+ scr_memsetw(p + vc->vc_cols - vc->state.x - nr, vc->vc_video_erase_char,
+ nr * 2);
+ vc->vc_need_wrap = 0;
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) p,
+ vc->vc_cols - vc->state.x);
+}
+
+static int softcursor_original = -1;
+
+static void add_softcursor(struct vc_data *vc)
+{
+ int i = scr_readw((u16 *) vc->vc_pos);
+ u32 type = vc->vc_cursor_type;
+
+ if (!(type & CUR_SW))
+ return;
+ if (softcursor_original != -1)
+ return;
+ softcursor_original = i;
+ i |= CUR_SET(type);
+ i ^= CUR_CHANGE(type);
+ if ((type & CUR_ALWAYS_BG) &&
+ (softcursor_original & CUR_BG) == (i & CUR_BG))
+ i ^= CUR_BG;
+ if ((type & CUR_INVERT_FG_BG) && (i & CUR_FG) == ((i & CUR_BG) >> 4))
+ i ^= CUR_FG;
+ scr_writew(i, (u16 *)vc->vc_pos);
+ if (con_should_update(vc))
+ con_putc(vc, i, vc->state.y, vc->state.x);
+}
+
+static void hide_softcursor(struct vc_data *vc)
+{
+ if (softcursor_original != -1) {
+ scr_writew(softcursor_original, (u16 *)vc->vc_pos);
+ if (con_should_update(vc))
+ con_putc(vc, softcursor_original, vc->state.y,
+ vc->state.x);
+ softcursor_original = -1;
+ }
+}
+
+static void hide_cursor(struct vc_data *vc)
+{
+ if (vc_is_sel(vc))
+ clear_selection();
+
+ vc->vc_sw->con_cursor(vc, false);
+ hide_softcursor(vc);
+}
+
+static void set_cursor(struct vc_data *vc)
+{
+ if (!con_is_fg(vc) || console_blanked || vc->vc_mode == KD_GRAPHICS)
+ return;
+ if (vc->vc_deccm) {
+ if (vc_is_sel(vc))
+ clear_selection();
+ add_softcursor(vc);
+ if (CUR_SIZE(vc->vc_cursor_type) != CUR_NONE)
+ vc->vc_sw->con_cursor(vc, true);
+ } 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->state.y +
+ 2 * vc->state.x;
+}
+
+static void save_screen(struct vc_data *vc)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (vc->vc_sw->con_save_screen)
+ vc->vc_sw->con_save_screen(vc);
+}
+
+static void flush_scrollback(struct vc_data *vc)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ set_origin(vc);
+ if (!con_is_visible(vc))
+ return;
+
+ /*
+ * The legacy way for flushing the scrollback buffer is to use a side
+ * effect of the con_switch method. We do it only on the foreground
+ * console as background consoles have no scrollback buffers in that
+ * case and we obviously don't want to switch to them.
+ */
+ hide_cursor(vc);
+ vc->vc_sw->con_switch(vc);
+ set_cursor(vc);
+}
+
+/*
+ * Redrawing of screen
+ */
+
+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);
+ }
+ if (tty0dev)
+ sysfs_notify(&tty0dev->kobj, NULL, "active");
+ } else {
+ hide_cursor(vc);
+ redraw = 1;
+ }
+
+ if (redraw) {
+ bool 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);
+ }
+
+ if (update && vc->vc_mode != KD_GRAPHICS)
+ do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
+ }
+ set_cursor(vc);
+ if (is_switch) {
+ vt_set_leds_compute_shiftstate();
+ notify_update(vc);
+ }
+}
+EXPORT_SYMBOL(redraw_screen);
+
+/*
+ * 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, bool init)
+{
+ /* ++Geert: vc->vc_sw->con_init determines console size */
+ if (vc->vc_sw)
+ module_put(vc->vc_sw->owner);
+ vc->vc_sw = conswitchp;
+
+ if (con_driver_map[num])
+ vc->vc_sw = con_driver_map[num];
+
+ __module_get(vc->vc_sw->owner);
+ vc->vc_num = num;
+ vc->vc_display_fg = &master_display_fg;
+ if (vc->uni_pagedict_loc)
+ con_free_unimap(vc);
+ vc->uni_pagedict_loc = &vc->uni_pagedict;
+ vc->uni_pagedict = NULL;
+ vc->vc_hi_font_mask = 0;
+ vc->vc_complement_mask = 0;
+ vc->vc_can_do_color = 0;
+ vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
+ 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;
+}
+
+
+static void visual_deinit(struct vc_data *vc)
+{
+ vc->vc_sw->con_deinit(vc);
+ module_put(vc->vc_sw->owner);
+}
+
+static void vc_port_destruct(struct tty_port *port)
+{
+ struct vc_data *vc = container_of(port, struct vc_data, port);
+
+ kfree(vc);
+}
+
+static const struct tty_port_operations vc_port_ops = {
+ .destruct = vc_port_destruct,
+};
+
+/*
+ * 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_MAXCOL (32767)
+#define VC_MAXROW (32767)
+
+int vc_allocate(unsigned int currcons) /* return 0 on success */
+{
+ struct vt_notifier_param param;
+ struct vc_data *vc;
+ int err;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (currcons >= MAX_NR_CONSOLES)
+ return -ENXIO;
+
+ if (vc_cons[currcons].d)
+ return 0;
+
+ /* due to the granularity of kmalloc, we waste some memory here */
+ /* the alloc is done in two steps, to optimize the common situation
+ of a 25x80 console (structsize=216, screenbuf_size=4000) */
+ /* although the numbers above are not valid since long ago, the
+ point is still up-to-date and the comment still has its value
+ even if only as a historical artifact. --mj, July 1998 */
+ param.vc = vc = kzalloc_obj(struct vc_data);
+ if (!vc)
+ return -ENOMEM;
+
+ vc_cons[currcons].d = vc;
+ tty_port_init(&vc->port);
+ vc->port.ops = &vc_port_ops;
+ INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+
+ visual_init(vc, currcons, true);
+
+ if (!*vc->uni_pagedict_loc)
+ con_set_default_unimap(vc);
+
+ err = -EINVAL;
+ if (vc->vc_cols > VC_MAXCOL || vc->vc_rows > VC_MAXROW ||
+ vc->vc_screenbuf_size > KMALLOC_MAX_SIZE || !vc->vc_screenbuf_size)
+ goto err_free;
+ err = -ENOMEM;
+ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_KERNEL);
+ if (!vc->vc_screenbuf)
+ goto err_free;
+
+ /* If no drivers have overridden us and the user didn't pass a
+ boot option, default to displaying the cursor */
+ if (global_cursor_default == -1)
+ global_cursor_default = 1;
+
+ vc_init(vc, 1);
+ vcs_make_sysfs(currcons);
+ atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
+
+ return 0;
+err_free:
+ visual_deinit(vc);
+ kfree(vc);
+ vc_cons[currcons].d = NULL;
+ return err;
+}
+
+static inline int resize_screen(struct vc_data *vc, int width, int height,
+ bool from_user)
+{
+ /* Resizes the resolution of the display adapater */
+ int err = 0;
+
+ if (vc->vc_sw->con_resize)
+ err = vc->vc_sw->con_resize(vc, width, height, from_user);
+
+ return err;
+}
+
+/**
+ * vc_do_resize - resizing method for the tty
+ * @tty: tty being resized
+ * @vc: virtual console private data
+ * @cols: columns
+ * @lines: lines
+ * @from_user: invoked by a user?
+ *
+ * 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.
+ *
+ * Locking: Caller must hold the console semaphore. Takes the termios rwsem 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, bool from_user)
+{
+ unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
+ unsigned long end;
+ unsigned int old_rows, old_row_size, first_copied_row;
+ unsigned int new_cols, new_rows, new_row_size, new_screen_size;
+ unsigned short *oldscreen, *newscreen;
+ u32 **new_uniscr = NULL;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (cols > VC_MAXCOL || lines > VC_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) {
+ /*
+ * This function is being called here to cover the case
+ * where the userspace calls the FBIOPUT_VSCREENINFO twice,
+ * passing the same fb_var_screeninfo containing the fields
+ * yres/xres equal to a number non-multiple of vc_font.height
+ * and yres_virtual/xres_virtual equal to number lesser than the
+ * vc_font.height and yres/xres.
+ * In the second call, the struct fb_var_screeninfo isn't
+ * being modified by the underlying driver because of the
+ * if above, and this causes the fbcon_display->vrows to become
+ * negative and it eventually leads to out-of-bound
+ * access by the imageblit function.
+ * To give the correct values to the struct and to not have
+ * to deal with possible errors from the code below, we call
+ * the resize_screen here as well.
+ */
+ return resize_screen(vc, new_cols, new_rows, from_user);
+ }
+
+ if (new_screen_size > KMALLOC_MAX_SIZE || !new_screen_size)
+ return -EINVAL;
+ newscreen = kzalloc(new_screen_size, GFP_USER);
+ if (!newscreen)
+ return -ENOMEM;
+
+ if (vc->vc_uni_lines) {
+ new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
+ if (!new_uniscr) {
+ kfree(newscreen);
+ return -ENOMEM;
+ }
+ }
+
+ if (vc_is_sel(vc))
+ clear_selection();
+
+ old_rows = vc->vc_rows;
+ old_row_size = vc->vc_size_row;
+
+ err = resize_screen(vc, new_cols, new_rows, from_user);
+ if (err) {
+ kfree(newscreen);
+ vc_uniscr_free(new_uniscr);
+ return err;
+ }
+
+ vc->vc_rows = new_rows;
+ vc->vc_cols = new_cols;
+ vc->vc_size_row = new_row_size;
+ vc->vc_screenbuf_size = new_screen_size;
+
+ rlth = min(old_row_size, new_row_size);
+ rrem = new_row_size - rlth;
+ old_origin = vc->vc_origin;
+ new_origin = (long) newscreen;
+ new_scr_end = new_origin + new_screen_size;
+
+ if (vc->state.y > new_rows) {
+ if (old_rows - vc->state.y < new_rows) {
+ /*
+ * Cursor near the bottom, copy contents from the
+ * bottom of buffer
+ */
+ first_copied_row = (old_rows - new_rows);
+ } else {
+ /*
+ * Cursor is in no man's land, copy 1/2 screenful
+ * from the top and bottom of cursor position
+ */
+ first_copied_row = (vc->state.y - new_rows/2);
+ }
+ old_origin += first_copied_row * old_row_size;
+ } else
+ first_copied_row = 0;
+ end = old_origin + old_row_size * min(old_rows, new_rows);
+
+ vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
+ vc->vc_uni_lines, rlth/2, first_copied_row,
+ min(old_rows, new_rows));
+ vc_uniscr_set(vc, new_uniscr);
+
+ update_attr(vc);
+
+ while (old_origin < end) {
+ scr_memcpyw((unsigned short *) new_origin,
+ (unsigned short *) old_origin, rlth);
+ if (rrem)
+ scr_memsetw((void *)(new_origin + rlth),
+ vc->vc_video_erase_char, rrem);
+ old_origin += old_row_size;
+ new_origin += new_row_size;
+ }
+ if (new_scr_end > new_origin)
+ scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
+ new_scr_end - new_origin);
+ oldscreen = vc->vc_screenbuf;
+ vc->vc_screenbuf = newscreen;
+ vc->vc_screenbuf_size = new_screen_size;
+ set_origin(vc);
+ kfree(oldscreen);
+
+ /* do part of a reset_terminal() */
+ vc->vc_top = 0;
+ vc->vc_bottom = vc->vc_rows;
+ gotoxy(vc, vc->state.x, vc->state.y);
+ save_cur(vc);
+
+ if (tty) {
+ /* Rewrite the requested winsize data with the actual
+ resulting sizes */
+ struct winsize ws;
+ memset(&ws, 0, sizeof(ws));
+ ws.ws_row = vc->vc_rows;
+ ws.ws_col = vc->vc_cols;
+ ws.ws_ypixel = vc->vc_scan_lines;
+ tty_do_resize(tty, &ws);
+ }
+
+ if (con_is_visible(vc))
+ update_screen(vc);
+ vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
+ notify_update(vc);
+ return err;
+}
+
+/**
+ * __vc_resize - resize a VT
+ * @vc: virtual console
+ * @cols: columns
+ * @rows: rows
+ * @from_user: invoked by a user?
+ *
+ * Resize a virtual console as seen from the console end of things. We use the
+ * common vc_do_resize() method to update the structures.
+ *
+ * Locking: 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,
+ bool from_user)
+{
+ return vc_do_resize(vc->port.tty, vc, cols, rows, from_user);
+}
+EXPORT_SYMBOL(__vc_resize);
+
+/**
+ * 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.
+ *
+ * Locking: Takes the console sem and the called methods then take the tty
+ * termios_rwsem 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;
+
+ guard(console_lock)();
+ return vc_do_resize(tty, vc, ws->ws_col, ws->ws_row, false);
+}
+
+struct vc_data *vc_deallocate(unsigned int currcons)
+{
+ struct vc_data *vc = NULL;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (vc_cons_allocated(currcons)) {
+ struct vt_notifier_param param;
+
+ param.vc = vc = vc_cons[currcons].d;
+ atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, &param);
+ vcs_remove_sysfs(currcons);
+ visual_deinit(vc);
+ con_free_unimap(vc);
+ put_pid(vc->vt_pid);
+ vc_uniscr_set(vc, NULL);
+ kfree(vc->vc_screenbuf);
+ vc_cons[currcons].d = NULL;
+ if (vc->vc_saved_screen != NULL) {
+ kfree(vc->vc_saved_screen);
+ vc->vc_saved_screen = NULL;
+ }
+ vc_uniscr_free(vc->vc_saved_uni_lines);
+ vc->vc_saved_uni_lines = NULL;
+ }
+ return vc;
+}
+
+/*
+ * VT102 emulator
+ */
+
+enum { EPecma = 0, EPdec, EPeq, EPgt, EPlt};
+
+#define set_kbd(vc, x) vt_set_kbd_mode_bit((vc)->vc_num, (x))
+#define clr_kbd(vc, x) vt_clr_kbd_mode_bit((vc)->vc_num, (x))
+#define is_kbd(vc, x) vt_get_kbd_mode_bit((vc)->vc_num, (x))
+
+#define decarm VC_REPEAT
+#define decckm VC_CKMODE
+#define kbdapplic VC_APPLIC
+#define lnm VC_CRLF
+
+const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
+ 8,12,10,14, 9,13,11,15 };
+EXPORT_SYMBOL(color_table);
+
+/* the default colour table, for VGA+ colour systems */
+unsigned char default_red[] = {
+ 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+ 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
+};
+module_param_array(default_red, byte, NULL, S_IRUGO | S_IWUSR);
+EXPORT_SYMBOL(default_red);
+
+unsigned char default_grn[] = {
+ 0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
+ 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
+};
+module_param_array(default_grn, byte, NULL, S_IRUGO | S_IWUSR);
+EXPORT_SYMBOL(default_grn);
+
+unsigned char default_blu[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
+};
+module_param_array(default_blu, byte, NULL, S_IRUGO | S_IWUSR);
+EXPORT_SYMBOL(default_blu);
+
+/*
+ * 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->state.x = 0;
+ else {
+ if (new_x >= vc->vc_cols)
+ vc->state.x = vc->vc_cols - 1;
+ else
+ vc->state.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->state.y = min_y;
+ else if (new_y >= max_y)
+ vc->state.y = max_y - 1;
+ else
+ vc->state.y = new_y;
+ vc->vc_pos = vc->vc_origin + vc->state.y * vc->vc_size_row +
+ (vc->state.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)
+{
+ scrolldelta(-(vc->vc_rows / 2));
+}
+
+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->state.y + 1 == vc->vc_bottom)
+ con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_UP, 1);
+ else if (vc->state.y < vc->vc_rows - 1) {
+ vc->state.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->state.y == vc->vc_top)
+ con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_DOWN, 1);
+ else if (vc->state.y > 0) {
+ vc->state.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->state.x << 1;
+ vc->vc_need_wrap = vc->state.x = 0;
+ notify_write(vc, '\r');
+}
+
+static inline void bs(struct vc_data *vc)
+{
+ if (vc->state.x) {
+ vc->vc_pos -= 2;
+ vc->state.x--;
+ vc->vc_need_wrap = 0;
+ notify_write(vc, '\b');
+ }
+}
+
+static inline void del(struct vc_data *vc)
+{
+ /* ignored */
+}
+
+enum CSI_J {
+ CSI_J_CURSOR_TO_END = 0,
+ CSI_J_START_TO_CURSOR = 1,
+ CSI_J_VISIBLE = 2,
+ CSI_J_FULL = 3,
+};
+
+static void csi_J(struct vc_data *vc, enum CSI_J vpar)
+{
+ unsigned short *start;
+ unsigned int count;
+
+ switch (vpar) {
+ case CSI_J_CURSOR_TO_END:
+ vc_uniscr_clear_line(vc, vc->state.x,
+ vc->vc_cols - vc->state.x);
+ vc_uniscr_clear_lines(vc, vc->state.y + 1,
+ vc->vc_rows - vc->state.y - 1);
+ count = (vc->vc_scr_end - vc->vc_pos) >> 1;
+ start = (unsigned short *)vc->vc_pos;
+ break;
+ case CSI_J_START_TO_CURSOR:
+ vc_uniscr_clear_line(vc, 0, vc->state.x + 1);
+ vc_uniscr_clear_lines(vc, 0, vc->state.y);
+ count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
+ start = (unsigned short *)vc->vc_origin;
+ break;
+ case CSI_J_FULL:
+ flush_scrollback(vc);
+ fallthrough;
+ case CSI_J_VISIBLE:
+ vc_uniscr_clear_lines(vc, 0, vc->vc_rows);
+ count = vc->vc_cols * vc->vc_rows;
+ start = (unsigned short *)vc->vc_origin;
+ break;
+ default:
+ return;
+ }
+ scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) start, count);
+ vc->vc_need_wrap = 0;
+}
+
+enum {
+ CSI_K_CURSOR_TO_LINEEND = 0,
+ CSI_K_LINESTART_TO_CURSOR = 1,
+ CSI_K_LINE = 2,
+};
+
+static void csi_K(struct vc_data *vc)
+{
+ unsigned int count;
+ unsigned short *start = (unsigned short *)vc->vc_pos;
+ int offset;
+
+ switch (vc->vc_par[0]) {
+ case CSI_K_CURSOR_TO_LINEEND:
+ offset = 0;
+ count = vc->vc_cols - vc->state.x;
+ break;
+ case CSI_K_LINESTART_TO_CURSOR:
+ offset = -vc->state.x;
+ count = vc->state.x + 1;
+ break;
+ case CSI_K_LINE:
+ offset = -vc->state.x;
+ count = vc->vc_cols;
+ break;
+ default:
+ return;
+ }
+ vc_uniscr_clear_line(vc, vc->state.x + offset, count);
+ scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count);
+ vc->vc_need_wrap = 0;
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long)(start + offset), count);
+}
+
+/* erase the following count positions */
+static void csi_X(struct vc_data *vc)
+{ /* not vt100? */
+ unsigned int count = clamp(vc->vc_par[0], 1, vc->vc_cols - vc->state.x);
+
+ vc_uniscr_clear_line(vc, vc->state.x, count);
+ scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count);
+ if (con_should_update(vc))
+ vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, count);
+ vc->vc_need_wrap = 0;
+}
+
+static void default_attr(struct vc_data *vc)
+{
+ vc->state.intensity = VCI_NORMAL;
+ vc->state.italic = false;
+ vc->state.underline = false;
+ vc->state.reverse = false;
+ vc->state.blink = false;
+ vc->state.color = vc->vc_def_color;
+}
+
+struct rgb { u8 r; u8 g; u8 b; };
+
+static void rgb_from_256(unsigned int i, struct rgb *c)
+{
+ if (i < 8) { /* Standard colours. */
+ c->r = i&1 ? 0xaa : 0x00;
+ c->g = i&2 ? 0xaa : 0x00;
+ c->b = i&4 ? 0xaa : 0x00;
+ } else if (i < 16) {
+ c->r = i&1 ? 0xff : 0x55;
+ c->g = i&2 ? 0xff : 0x55;
+ c->b = i&4 ? 0xff : 0x55;
+ } else if (i < 232) { /* 6x6x6 colour cube. */
+ i -= 16;
+ c->b = i % 6 * 255 / 6;
+ i /= 6;
+ c->g = i % 6 * 255 / 6;
+ i /= 6;
+ c->r = i * 255 / 6;
+ } else /* Grayscale ramp. */
+ c->r = c->g = c->b = i * 10 - 2312;
+}
+
+static void rgb_foreground(struct vc_data *vc, const struct rgb *c)
+{
+ u8 hue = 0, max = max3(c->r, c->g, c->b);
+
+ if (c->r > max / 2)
+ hue |= 4;
+ if (c->g > max / 2)
+ hue |= 2;
+ if (c->b > max / 2)
+ hue |= 1;
+
+ if (hue == 7 && max <= 0x55) {
+ hue = 0;
+ vc->state.intensity = VCI_BOLD;
+ } else if (max > 0xaa)
+ vc->state.intensity = VCI_BOLD;
+ else
+ vc->state.intensity = VCI_NORMAL;
+
+ vc->state.color = (vc->state.color & 0xf0) | hue;
+}
+
+static void rgb_background(struct vc_data *vc, const struct rgb *c)
+{
+ /* For backgrounds, err on the dark side. */
+ vc->state.color = (vc->state.color & 0x0f)
+ | (c->r&0x80) >> 1 | (c->g&0x80) >> 2 | (c->b&0x80) >> 3;
+}
+
+/*
+ * ITU T.416 Higher colour modes. They break the usual properties of SGR codes
+ * and thus need to be detected and ignored by hand.
+ *
+ * Subcommands 3 (CMY) and 4 (CMYK) are so insane there's no point in
+ * supporting them.
+ */
+static int vc_t416_color(struct vc_data *vc, int i,
+ void(*set_color)(struct vc_data *vc, const struct rgb *c))
+{
+ struct rgb c;
+
+ i++;
+ if (i > vc->vc_npar)
+ return i;
+
+ if (vc->vc_par[i] == 5 && i + 1 <= vc->vc_npar) {
+ /* 256 colours */
+ i++;
+ rgb_from_256(vc->vc_par[i], &c);
+ } else if (vc->vc_par[i] == 2 && i + 3 <= vc->vc_npar) {
+ /* 24 bit */
+ c.r = vc->vc_par[i + 1];
+ c.g = vc->vc_par[i + 2];
+ c.b = vc->vc_par[i + 3];
+ i += 3;
+ } else
+ return i;
+
+ set_color(vc, &c);
+
+ return i;
+}
+
+enum {
+ CSI_m_DEFAULT = 0,
+ CSI_m_BOLD = 1,
+ CSI_m_HALF_BRIGHT = 2,
+ CSI_m_ITALIC = 3,
+ CSI_m_UNDERLINE = 4,
+ CSI_m_BLINK = 5,
+ CSI_m_REVERSE = 7,
+ CSI_m_PRI_FONT = 10,
+ CSI_m_ALT_FONT1 = 11,
+ CSI_m_ALT_FONT2 = 12,
+ CSI_m_DOUBLE_UNDERLINE = 21,
+ CSI_m_NORMAL_INTENSITY = 22,
+ CSI_m_NO_ITALIC = 23,
+ CSI_m_NO_UNDERLINE = 24,
+ CSI_m_NO_BLINK = 25,
+ CSI_m_NO_REVERSE = 27,
+ CSI_m_FG_COLOR_BEG = 30,
+ CSI_m_FG_COLOR_END = 37,
+ CSI_m_FG_COLOR = 38,
+ CSI_m_DEFAULT_FG_COLOR = 39,
+ CSI_m_BG_COLOR_BEG = 40,
+ CSI_m_BG_COLOR_END = 47,
+ CSI_m_BG_COLOR = 48,
+ CSI_m_DEFAULT_BG_COLOR = 49,
+ CSI_m_UNDERLINE_COLOR = 58,
+ CSI_m_BRIGHT_FG_COLOR_BEG = 90,
+ CSI_m_BRIGHT_FG_COLOR_END = 97,
+ CSI_m_BRIGHT_FG_COLOR_OFF = CSI_m_BRIGHT_FG_COLOR_BEG - CSI_m_FG_COLOR_BEG,
+ CSI_m_BRIGHT_BG_COLOR_BEG = 100,
+ CSI_m_BRIGHT_BG_COLOR_END = 107,
+ CSI_m_BRIGHT_BG_COLOR_OFF = CSI_m_BRIGHT_BG_COLOR_BEG - CSI_m_BG_COLOR_BEG,
+};
+
+/* console_lock 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 CSI_m_DEFAULT: /* all attributes off */
+ default_attr(vc);
+ break;
+ case CSI_m_BOLD:
+ vc->state.intensity = VCI_BOLD;
+ break;
+ case CSI_m_HALF_BRIGHT:
+ vc->state.intensity = VCI_HALF_BRIGHT;
+ break;
+ case CSI_m_ITALIC:
+ vc->state.italic = true;
+ break;
+ case CSI_m_DOUBLE_UNDERLINE:
+ /*
+ * No console drivers support double underline, so
+ * convert it to a single underline.
+ */
+ case CSI_m_UNDERLINE:
+ vc->state.underline = true;
+ break;
+ case CSI_m_BLINK:
+ vc->state.blink = true;
+ break;
+ case CSI_m_REVERSE:
+ vc->state.reverse = true;
+ break;
+ case CSI_m_PRI_FONT: /* 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->state.Gx_charset[vc->state.charset], vc);
+ vc->vc_disp_ctrl = 0;
+ vc->vc_toggle_meta = 0;
+ break;
+ case CSI_m_ALT_FONT1: /* 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 CSI_m_ALT_FONT2: /* 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 CSI_m_NORMAL_INTENSITY:
+ vc->state.intensity = VCI_NORMAL;
+ break;
+ case CSI_m_NO_ITALIC:
+ vc->state.italic = false;
+ break;
+ case CSI_m_NO_UNDERLINE:
+ vc->state.underline = false;
+ break;
+ case CSI_m_NO_BLINK:
+ vc->state.blink = false;
+ break;
+ case CSI_m_NO_REVERSE:
+ vc->state.reverse = false;
+ break;
+ case CSI_m_FG_COLOR:
+ i = vc_t416_color(vc, i, rgb_foreground);
+ break;
+ case CSI_m_BG_COLOR:
+ i = vc_t416_color(vc, i, rgb_background);
+ break;
+ case CSI_m_DEFAULT_FG_COLOR:
+ vc->state.color = (vc->vc_def_color & 0x0f) |
+ (vc->state.color & 0xf0);
+ break;
+ case CSI_m_DEFAULT_BG_COLOR:
+ vc->state.color = (vc->vc_def_color & 0xf0) |
+ (vc->state.color & 0x0f);
+ break;
+ case CSI_m_BRIGHT_FG_COLOR_BEG ... CSI_m_BRIGHT_FG_COLOR_END:
+ vc->state.intensity = VCI_BOLD;
+ vc->vc_par[i] -= CSI_m_BRIGHT_FG_COLOR_OFF;
+ fallthrough;
+ case CSI_m_FG_COLOR_BEG ... CSI_m_FG_COLOR_END:
+ vc->vc_par[i] -= CSI_m_FG_COLOR_BEG;
+ vc->state.color = color_table[vc->vc_par[i]] |
+ (vc->state.color & 0xf0);
+ break;
+ case CSI_m_BRIGHT_BG_COLOR_BEG ... CSI_m_BRIGHT_BG_COLOR_END:
+ vc->vc_par[i] -= CSI_m_BRIGHT_BG_COLOR_OFF;
+ fallthrough;
+ case CSI_m_BG_COLOR_BEG ... CSI_m_BG_COLOR_END:
+ vc->vc_par[i] -= CSI_m_BG_COLOR_BEG;
+ vc->state.color = (color_table[vc->vc_par[i]] << 4) |
+ (vc->state.color & 0x0f);
+ break;
+ }
+ update_attr(vc);
+}
+
+static void respond_string(const char *p, size_t len, struct tty_port *port)
+{
+ tty_insert_flip_string(port, p, len);
+ tty_flip_buffer_push(port);
+}
+
+static void cursor_report(struct vc_data *vc, struct tty_struct *tty)
+{
+ char buf[40];
+ int len;
+
+ len = sprintf(buf, "\033[%d;%dR", vc->state.y +
+ (vc->vc_decom ? vc->vc_top + 1 : 1),
+ vc->state.x + 1);
+ respond_string(buf, len, tty->port);
+}
+
+static inline void status_report(struct tty_struct *tty)
+{
+ static const char teminal_ok[] = "\033[0n";
+
+ respond_string(teminal_ok, strlen(teminal_ok), tty->port);
+}
+
+static inline void respond_ID(struct tty_struct *tty)
+{
+ /* terminal answer to an ESC-Z or csi0c query. */
+ static const char vt102_id[] = "\033[?6c";
+
+ respond_string(vt102_id, strlen(vt102_id), tty->port);
+}
+
+void mouse_report(struct tty_struct *tty, int butt, int mrx, int mry)
+{
+ char buf[8];
+ int len;
+
+ len = sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt),
+ (char)('!' + mrx), (char)('!' + mry));
+ respond_string(buf, len, tty->port);
+}
+
+/* invoked via ioctl(TIOCLINUX) and through set_selection_user */
+int mouse_reporting(void)
+{
+ return vc_cons[fg_console].d->vc_report_mouse;
+}
+
+/* invoked via ioctl(TIOCLINUX) */
+static int get_bracketed_paste(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+
+ return vc->vc_bracketed_paste;
+}
+
+/* console_lock is held */
+static void enter_alt_screen(struct vc_data *vc)
+{
+ unsigned int size = vc->vc_rows * vc->vc_cols * 2;
+
+ if (vc->vc_saved_screen != NULL)
+ return; /* Already inside an alt-screen */
+ vc->vc_saved_screen = kmemdup((u16 *)vc->vc_origin, size, GFP_KERNEL);
+ if (vc->vc_saved_screen == NULL)
+ return;
+ vc->vc_saved_uni_lines = vc->vc_uni_lines;
+ vc->vc_uni_lines = NULL;
+ vc->vc_saved_rows = vc->vc_rows;
+ vc->vc_saved_cols = vc->vc_cols;
+ save_cur(vc);
+ /* clear entire screen */
+ csi_J(vc, CSI_J_FULL);
+}
+
+/* console_lock is held */
+static void leave_alt_screen(struct vc_data *vc)
+{
+ unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows);
+ unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols);
+ u16 *src, *dest;
+
+ if (vc->vc_saved_screen == NULL)
+ return; /* Not inside an alt-screen */
+ for (unsigned int r = 0; r < rows; r++) {
+ src = vc->vc_saved_screen + r * vc->vc_saved_cols;
+ dest = ((u16 *)vc->vc_origin) + r * vc->vc_cols;
+ memcpy(dest, src, 2 * cols);
+ }
+ /*
+ * If the console was resized while in the alternate screen,
+ * resize the saved unicode buffer to the current dimensions.
+ * On allocation failure new_uniscr is NULL, causing the old
+ * buffer to be freed and vc_uni_lines to be lazily rebuilt
+ * via vc_uniscr_check() when next needed.
+ */
+ if (vc->vc_saved_uni_lines &&
+ (vc->vc_saved_rows != vc->vc_rows ||
+ vc->vc_saved_cols != vc->vc_cols)) {
+ u32 **new_uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+
+ if (new_uniscr)
+ vc_uniscr_copy_area(new_uniscr, vc->vc_cols, vc->vc_rows,
+ vc->vc_saved_uni_lines, cols, 0, rows);
+ vc_uniscr_free(vc->vc_saved_uni_lines);
+ vc->vc_saved_uni_lines = new_uniscr;
+ }
+ vc_uniscr_set(vc, vc->vc_saved_uni_lines);
+ vc->vc_saved_uni_lines = NULL;
+ restore_cur(vc);
+ /* Update the entire screen */
+ if (con_should_update(vc))
+ do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
+ kfree(vc->vc_saved_screen);
+ vc->vc_saved_screen = NULL;
+}
+
+enum {
+ CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */
+ CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */
+ CSI_DEC_hl_REVERSE_VIDEO = 5, /* SCNM */
+ CSI_DEC_hl_ORIGIN_MODE = 6, /* OM: origin relative/absolute */
+ CSI_DEC_hl_AUTOWRAP = 7, /* AWM */
+ CSI_DEC_hl_AUTOREPEAT = 8, /* ARM */
+ CSI_DEC_hl_MOUSE_X10 = 9,
+ CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */
+ CSI_DEC_hl_MOUSE_VT200 = 1000,
+ CSI_DEC_hl_ALT_SCREEN = 1049,
+ CSI_DEC_hl_BRACKETED_PASTE = 2004,
+};
+
+/* console_lock is held */
+static void csi_DEC_hl(struct vc_data *vc, bool on_off)
+{
+ unsigned int i;
+
+ for (i = 0; i <= vc->vc_npar; i++)
+ switch (vc->vc_par[i]) {
+ case CSI_DEC_hl_CURSOR_KEYS:
+ if (on_off)
+ set_kbd(vc, decckm);
+ else
+ clr_kbd(vc, decckm);
+ break;
+ case CSI_DEC_hl_132_COLUMNS: /* unimplemented */
+#if 0
+ vc_resize(deccolm ? 132 : 80, vc->vc_rows);
+ /* this alone does not suffice; some user mode
+ utility has to change the hardware regs */
+#endif
+ break;
+ case CSI_DEC_hl_REVERSE_VIDEO:
+ if (vc->vc_decscnm != on_off) {
+ vc->vc_decscnm = on_off;
+ invert_screen(vc, 0, vc->vc_screenbuf_size,
+ false);
+ update_attr(vc);
+ }
+ break;
+ case CSI_DEC_hl_ORIGIN_MODE:
+ vc->vc_decom = on_off;
+ gotoxay(vc, 0, 0);
+ break;
+ case CSI_DEC_hl_AUTOWRAP:
+ vc->vc_decawm = on_off;
+ break;
+ case CSI_DEC_hl_AUTOREPEAT:
+ if (on_off)
+ set_kbd(vc, decarm);
+ else
+ clr_kbd(vc, decarm);
+ break;
+ case CSI_DEC_hl_MOUSE_X10:
+ vc->vc_report_mouse = on_off ? 1 : 0;
+ break;
+ case CSI_DEC_hl_SHOW_CURSOR:
+ vc->vc_deccm = on_off;
+ break;
+ case CSI_DEC_hl_MOUSE_VT200:
+ vc->vc_report_mouse = on_off ? 2 : 0;
+ break;
+ case CSI_DEC_hl_BRACKETED_PASTE:
+ vc->vc_bracketed_paste = on_off;
+ break;
+ case CSI_DEC_hl_ALT_SCREEN:
+ if (on_off)
+ enter_alt_screen(vc);
+ else
+ leave_alt_screen(vc);
+ break;
+ }
+}
+
+enum {
+ CSI_hl_DISPLAY_CTRL = 3, /* handle ansi control chars */
+ CSI_hl_INSERT = 4, /* IRM: insert/replace */
+ CSI_hl_AUTO_NL = 20, /* LNM: Enter == CrLf/Lf */
+};
+
+/* console_lock is held */
+static void csi_hl(struct vc_data *vc, bool on_off)
+{
+ unsigned int i;
+
+ for (i = 0; i <= vc->vc_npar; i++)
+ switch (vc->vc_par[i]) { /* ANSI modes set/reset */
+ case CSI_hl_DISPLAY_CTRL:
+ vc->vc_disp_ctrl = on_off;
+ break;
+ case CSI_hl_INSERT:
+ vc->vc_decim = on_off;
+ break;
+ case CSI_hl_AUTO_NL:
+ if (on_off)
+ set_kbd(vc, lnm);
+ else
+ clr_kbd(vc, lnm);
+ break;
+ }
+}
+
+enum CSI_right_square_bracket {
+ CSI_RSB_COLOR_FOR_UNDERLINE = 1,
+ CSI_RSB_COLOR_FOR_HALF_BRIGHT = 2,
+ CSI_RSB_MAKE_CUR_COLOR_DEFAULT = 8,
+ CSI_RSB_BLANKING_INTERVAL = 9,
+ CSI_RSB_BELL_FREQUENCY = 10,
+ CSI_RSB_BELL_DURATION = 11,
+ CSI_RSB_BRING_CONSOLE_TO_FRONT = 12,
+ CSI_RSB_UNBLANK = 13,
+ CSI_RSB_VESA_OFF_INTERVAL = 14,
+ CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT = 15,
+ CSI_RSB_CURSOR_BLINK_INTERVAL = 16,
+};
+
+/*
+ * csi_RSB - csi+] (Right Square Bracket) handler
+ *
+ * These are linux console private sequences.
+ *
+ * console_lock is held
+ */
+static void csi_RSB(struct vc_data *vc)
+{
+ switch (vc->vc_par[0]) {
+ case CSI_RSB_COLOR_FOR_UNDERLINE:
+ if (vc->vc_can_do_color && vc->vc_par[1] < 16) {
+ vc->vc_ulcolor = color_table[vc->vc_par[1]];
+ if (vc->state.underline)
+ update_attr(vc);
+ }
+ break;
+ case CSI_RSB_COLOR_FOR_HALF_BRIGHT:
+ if (vc->vc_can_do_color && vc->vc_par[1] < 16) {
+ vc->vc_halfcolor = color_table[vc->vc_par[1]];
+ if (vc->state.intensity == VCI_HALF_BRIGHT)
+ update_attr(vc);
+ }
+ break;
+ case CSI_RSB_MAKE_CUR_COLOR_DEFAULT:
+ 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 CSI_RSB_BLANKING_INTERVAL:
+ blankinterval = min(vc->vc_par[1], 60U) * 60;
+ poke_blanked_console();
+ break;
+ case CSI_RSB_BELL_FREQUENCY:
+ if (vc->vc_npar >= 1)
+ vc->vc_bell_pitch = vc->vc_par[1];
+ else
+ vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
+ break;
+ case CSI_RSB_BELL_DURATION:
+ if (vc->vc_npar >= 1)
+ vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
+ msecs_to_jiffies(vc->vc_par[1]) : 0;
+ else
+ vc->vc_bell_duration = DEFAULT_BELL_DURATION;
+ break;
+ case CSI_RSB_BRING_CONSOLE_TO_FRONT:
+ if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1))
+ set_console(vc->vc_par[1] - 1);
+ break;
+ case CSI_RSB_UNBLANK:
+ poke_blanked_console();
+ break;
+ case CSI_RSB_VESA_OFF_INTERVAL:
+ vesa_off_interval = min(vc->vc_par[1], 60U) * 60 * HZ;
+ break;
+ case CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT:
+ set_console(last_console);
+ break;
+ case CSI_RSB_CURSOR_BLINK_INTERVAL:
+ if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 &&
+ vc->vc_par[1] <= USHRT_MAX)
+ vc->vc_cur_blink_ms = vc->vc_par[1];
+ else
+ vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
+ break;
+ }
+}
+
+/* console_lock is held */
+static void csi_at(struct vc_data *vc, unsigned int nr)
+{
+ nr = clamp(nr, 1, vc->vc_cols - vc->state.x);
+ insert_char(vc, nr);
+}
+
+/* console_lock is held */
+static void csi_L(struct vc_data *vc)
+{
+ unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_rows - vc->state.y);
+
+ con_scroll(vc, vc->state.y, vc->vc_bottom, SM_DOWN, nr);
+ vc->vc_need_wrap = 0;
+}
+
+/* console_lock is held */
+static void csi_P(struct vc_data *vc)
+{
+ unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_cols - vc->state.x);
+
+ delete_char(vc, nr);
+}
+
+/* console_lock is held */
+static void csi_M(struct vc_data *vc)
+{
+ unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_rows - vc->state.y);
+
+ con_scroll(vc, vc->state.y, vc->vc_bottom, SM_UP, nr);
+ vc->vc_need_wrap = 0;
+}
+
+/* console_lock is held (except via vc_init->reset_terminal */
+static void save_cur(struct vc_data *vc)
+{
+ memcpy(&vc->saved_state, &vc->state, sizeof(vc->state));
+}
+
+/* console_lock is held */
+static void restore_cur(struct vc_data *vc)
+{
+ memcpy(&vc->state, &vc->saved_state, sizeof(vc->state));
+
+ gotoxy(vc, vc->state.x, vc->state.y);
+ vc->vc_translate = set_translate(vc->state.Gx_charset[vc->state.charset],
+ vc);
+ update_attr(vc);
+ vc->vc_need_wrap = 0;
+}
+
+/**
+ * enum vc_ctl_state - control characters state of a vt
+ *
+ * @ESnormal: initial state, no control characters parsed
+ * @ESesc: ESC parsed
+ * @ESsquare: CSI parsed -- modifiers/parameters/ctrl chars expected
+ * @ESgetpars: CSI parsed -- parameters/ctrl chars expected
+ * @ESgetsubpars: CSI m parsed -- subparameters expected
+ * @ESfunckey: CSI [ parsed
+ * @EShash: ESC # parsed
+ * @ESsetG0: ESC ( parsed
+ * @ESsetG1: ESC ) parsed
+ * @ESpercent: ESC % parsed
+ * @EScsiignore: CSI [0x20-0x3f] parsed
+ * @ESnonstd: OSC parsed
+ * @ESpalette: OSC P parsed
+ * @ESosc: OSC [0-9] parsed
+ * @ESANSI_first: first state for ignoring ansi control sequences
+ * @ESapc: ESC _ parsed
+ * @ESpm: ESC ^ parsed
+ * @ESdcs: ESC P parsed
+ * @ESANSI_last: last state for ignoring ansi control sequences
+ */
+enum vc_ctl_state {
+ ESnormal,
+ ESesc,
+ ESsquare,
+ ESgetpars,
+ ESgetsubpars,
+ ESfunckey,
+ EShash,
+ ESsetG0,
+ ESsetG1,
+ ESpercent,
+ EScsiignore,
+ ESnonstd,
+ ESpalette,
+ ESosc,
+ ESANSI_first = ESosc,
+ ESapc,
+ ESpm,
+ ESdcs,
+ ESANSI_last = ESdcs,
+};
+
+/* console_lock is held (except via vc_init()) */
+static void reset_terminal(struct vc_data *vc, int do_clear)
+{
+ unsigned int i;
+
+ vc->vc_top = 0;
+ vc->vc_bottom = vc->vc_rows;
+ vc->vc_state = ESnormal;
+ vc->vc_priv = EPecma;
+ vc->vc_translate = set_translate(LAT1_MAP, vc);
+ vc->state.Gx_charset[0] = LAT1_MAP;
+ vc->state.Gx_charset[1] = GRAF_MAP;
+ vc->state.charset = 0;
+ vc->vc_need_wrap = 0;
+ vc->vc_report_mouse = 0;
+ vc->vc_bracketed_paste = 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;
+
+ if (vc->vc_saved_screen != NULL) {
+ kfree(vc->vc_saved_screen);
+ vc->vc_saved_screen = NULL;
+ vc_uniscr_free(vc->vc_saved_uni_lines);
+ vc->vc_saved_uni_lines = NULL;
+ vc->vc_saved_rows = 0;
+ vc->vc_saved_cols = 0;
+ }
+
+ vt_reset_keyboard(vc->vc_num);
+
+ vc->vc_cursor_type = cur_default;
+ vc->vc_complement_mask = vc->vc_s_complement_mask;
+
+ default_attr(vc);
+ update_attr(vc);
+
+ bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT);
+ for (i = 0; i < VC_TABSTOPS_COUNT; i += 8)
+ set_bit(i, vc->vc_tab_stop);
+
+ vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
+ vc->vc_bell_duration = DEFAULT_BELL_DURATION;
+ vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
+
+ gotoxy(vc, 0, 0);
+ save_cur(vc);
+ if (do_clear)
+ csi_J(vc, CSI_J_VISIBLE);
+}
+
+static void vc_setGx(struct vc_data *vc, unsigned int which, u8 c)
+{
+ unsigned char *charset = &vc->state.Gx_charset[which];
+
+ switch (c) {
+ case '0':
+ *charset = GRAF_MAP;
+ break;
+ case 'B':
+ *charset = LAT1_MAP;
+ break;
+ case 'U':
+ *charset = IBMPC_MAP;
+ break;
+ case 'K':
+ *charset = USER_MAP;
+ break;
+ }
+
+ if (vc->state.charset == which)
+ vc->vc_translate = set_translate(*charset, vc);
+}
+
+static bool ansi_control_string(enum vc_ctl_state state)
+{
+ return state >= ESANSI_first && state <= ESANSI_last;
+}
+
+enum {
+ ASCII_NULL = 0,
+ ASCII_BELL = 7,
+ ASCII_BACKSPACE = 8,
+ ASCII_IGNORE_FIRST = ASCII_BACKSPACE,
+ ASCII_HTAB = 9,
+ ASCII_LINEFEED = 10,
+ ASCII_VTAB = 11,
+ ASCII_FORMFEED = 12,
+ ASCII_CAR_RET = 13,
+ ASCII_IGNORE_LAST = ASCII_CAR_RET,
+ ASCII_SHIFTOUT = 14,
+ ASCII_SHIFTIN = 15,
+ ASCII_CANCEL = 24,
+ ASCII_SUBSTITUTE = 26,
+ ASCII_ESCAPE = 27,
+ ASCII_CSI_IGNORE_FIRST = ' ', /* 0x2x, 0x3a and 0x3c - 0x3f */
+ ASCII_CSI_IGNORE_LAST = '?',
+ ASCII_DEL = 127,
+ ASCII_EXT_CSI = 128 + ASCII_ESCAPE,
+};
+
+/*
+ * Handle ascii characters in control sequences and change states accordingly.
+ * E.g. ESC sets the state of vc to ESesc.
+ *
+ * Returns: true if @c handled.
+ */
+static bool handle_ascii(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ switch (c) {
+ case ASCII_NULL:
+ return true;
+ case ASCII_BELL:
+ if (ansi_control_string(vc->vc_state))
+ vc->vc_state = ESnormal;
+ else if (vc->vc_bell_duration)
+ kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration);
+ return true;
+ case ASCII_BACKSPACE:
+ bs(vc);
+ return true;
+ case ASCII_HTAB:
+ vc->vc_pos -= (vc->state.x << 1);
+
+ vc->state.x = find_next_bit(vc->vc_tab_stop,
+ min(vc->vc_cols - 1, VC_TABSTOPS_COUNT),
+ vc->state.x + 1);
+ if (vc->state.x >= VC_TABSTOPS_COUNT)
+ vc->state.x = vc->vc_cols - 1;
+
+ vc->vc_pos += (vc->state.x << 1);
+ notify_write(vc, '\t');
+ return true;
+ case ASCII_LINEFEED:
+ case ASCII_VTAB:
+ case ASCII_FORMFEED:
+ lf(vc);
+ if (!is_kbd(vc, lnm))
+ return true;
+ fallthrough;
+ case ASCII_CAR_RET:
+ cr(vc);
+ return true;
+ case ASCII_SHIFTOUT:
+ vc->state.charset = 1;
+ vc->vc_translate = set_translate(vc->state.Gx_charset[1], vc);
+ vc->vc_disp_ctrl = 1;
+ return true;
+ case ASCII_SHIFTIN:
+ vc->state.charset = 0;
+ vc->vc_translate = set_translate(vc->state.Gx_charset[0], vc);
+ vc->vc_disp_ctrl = 0;
+ return true;
+ case ASCII_CANCEL:
+ case ASCII_SUBSTITUTE:
+ vc->vc_state = ESnormal;
+ return true;
+ case ASCII_ESCAPE:
+ vc->vc_state = ESesc;
+ return true;
+ case ASCII_DEL:
+ del(vc);
+ return true;
+ case ASCII_EXT_CSI:
+ vc->vc_state = ESsquare;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Handle a character (@c) following an ESC (when @vc is in the ESesc state).
+ * E.g. previous ESC with @c == '[' here yields the ESsquare state (that is:
+ * CSI).
+ */
+static void handle_esc(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ vc->vc_state = ESnormal;
+ switch (c) {
+ case '[':
+ vc->vc_state = ESsquare;
+ break;
+ case ']':
+ vc->vc_state = ESnonstd;
+ break;
+ case '_':
+ vc->vc_state = ESapc;
+ break;
+ case '^':
+ vc->vc_state = ESpm;
+ break;
+ case '%':
+ vc->vc_state = ESpercent;
+ break;
+ case 'E':
+ cr(vc);
+ lf(vc);
+ break;
+ case 'M':
+ ri(vc);
+ break;
+ case 'D':
+ lf(vc);
+ break;
+ case 'H':
+ if (vc->state.x < VC_TABSTOPS_COUNT)
+ set_bit(vc->state.x, vc->vc_tab_stop);
+ break;
+ case 'P':
+ vc->vc_state = ESdcs;
+ break;
+ case 'Z':
+ respond_ID(tty);
+ break;
+ case '7':
+ save_cur(vc);
+ break;
+ case '8':
+ restore_cur(vc);
+ break;
+ case '(':
+ vc->vc_state = ESsetG0;
+ break;
+ case ')':
+ vc->vc_state = ESsetG1;
+ break;
+ case '#':
+ vc->vc_state = EShash;
+ break;
+ case 'c':
+ reset_terminal(vc, 1);
+ break;
+ case '>': /* Numeric keypad */
+ clr_kbd(vc, kbdapplic);
+ break;
+ case '=': /* Appl. keypad */
+ set_kbd(vc, kbdapplic);
+ break;
+ }
+}
+
+/*
+ * Handle special DEC control sequences ("ESC [ ? parameters char"). Parameters
+ * are in @vc->vc_par and the char is in @c here.
+ */
+static void csi_DEC(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ switch (c) {
+ case 'h':
+ csi_DEC_hl(vc, true);
+ break;
+ case 'l':
+ csi_DEC_hl(vc, false);
+ break;
+ case 'c':
+ if (vc->vc_par[0])
+ vc->vc_cursor_type = CUR_MAKE(vc->vc_par[0],
+ vc->vc_par[1],
+ vc->vc_par[2]);
+ else
+ vc->vc_cursor_type = cur_default;
+ break;
+ case 'm':
+ 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;
+ break;
+ case 'n':
+ if (vc->vc_par[0] == 5)
+ status_report(tty);
+ else if (vc->vc_par[0] == 6)
+ cursor_report(vc, tty);
+ break;
+ }
+}
+
+/*
+ * Handle Control Sequence Introducer control characters. That is
+ * "ESC [ parameters char". Parameters are in @vc->vc_par and the char is in
+ * @c here.
+ */
+static void csi_ECMA(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ switch (c) {
+ case 'G':
+ case '`':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ gotoxy(vc, vc->vc_par[0], vc->state.y);
+ break;
+ case 'A':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x, vc->state.y - vc->vc_par[0]);
+ break;
+ case 'B':
+ case 'e':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x, vc->state.y + vc->vc_par[0]);
+ break;
+ case 'C':
+ case 'a':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x + vc->vc_par[0], vc->state.y);
+ break;
+ case 'D':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x - vc->vc_par[0], vc->state.y);
+ break;
+ case 'E':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, 0, vc->state.y + vc->vc_par[0]);
+ break;
+ case 'F':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, 0, vc->state.y - vc->vc_par[0]);
+ break;
+ case 'd':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ gotoxay(vc, vc->state.x ,vc->vc_par[0]);
+ break;
+ 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]);
+ break;
+ case 'J':
+ csi_J(vc, vc->vc_par[0]);
+ break;
+ case 'K':
+ csi_K(vc);
+ break;
+ case 'L':
+ csi_L(vc);
+ break;
+ case 'M':
+ csi_M(vc);
+ break;
+ case 'P':
+ csi_P(vc);
+ break;
+ case 'c':
+ if (!vc->vc_par[0])
+ respond_ID(tty);
+ break;
+ case 'g':
+ if (!vc->vc_par[0] && vc->state.x < VC_TABSTOPS_COUNT)
+ set_bit(vc->state.x, vc->vc_tab_stop);
+ else if (vc->vc_par[0] == 3)
+ bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT);
+ break;
+ case 'h':
+ csi_hl(vc, true);
+ break;
+ case 'l':
+ csi_hl(vc, false);
+ break;
+ case 'm':
+ csi_m(vc);
+ break;
+ case 'n':
+ if (vc->vc_par[0] == 5)
+ status_report(tty);
+ else if (vc->vc_par[0] == 6)
+ cursor_report(vc, tty);
+ break;
+ case 'q': /* DECLL - but only 3 leds */
+ /* map 0,1,2,3 to 0,1,2,4 */
+ if (vc->vc_par[0] < 4)
+ vt_set_led_state(vc->vc_num,
+ (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4);
+ break;
+ 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);
+ }
+ break;
+ case 's':
+ save_cur(vc);
+ break;
+ case 'u':
+ restore_cur(vc);
+ break;
+ case 'X':
+ csi_X(vc);
+ break;
+ case '@':
+ csi_at(vc, vc->vc_par[0]);
+ break;
+ case ']':
+ csi_RSB(vc);
+ break;
+ }
+
+}
+
+static void vc_reset_params(struct vc_data *vc)
+{
+ memset(vc->vc_par, 0, sizeof(vc->vc_par));
+ vc->vc_npar = 0;
+}
+
+/* console_lock is held */
+static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ /*
+ * Control characters can be used in the _middle_
+ * of an escape sequence, aside from ANSI control strings.
+ */
+ if (ansi_control_string(vc->vc_state) && c >= ASCII_IGNORE_FIRST &&
+ c <= ASCII_IGNORE_LAST)
+ return;
+
+ if (handle_ascii(tty, vc, c))
+ return;
+
+ switch(vc->vc_state) {
+ case ESesc: /* ESC */
+ handle_esc(tty, vc, c);
+ return;
+ case ESnonstd: /* ESC ] aka OSC */
+ switch (c) {
+ case 'P': /* palette escape sequence */
+ vc_reset_params(vc);
+ vc->vc_state = ESpalette;
+ return;
+ case 'R': /* reset palette */
+ reset_palette(vc);
+ break;
+ case '0' ... '9':
+ vc->vc_state = ESosc;
+ return;
+ }
+ vc->vc_state = ESnormal;
+ return;
+ case ESpalette: /* ESC ] P aka OSC P */
+ 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: /* ESC [ aka CSI, parameters or modifiers expected */
+ vc_reset_params(vc);
+
+ vc->vc_state = ESgetpars;
+ switch (c) {
+ case '[': /* Function key */
+ vc->vc_state = ESfunckey;
+ return;
+ case '?':
+ vc->vc_priv = EPdec;
+ return;
+ case '>':
+ vc->vc_priv = EPgt;
+ return;
+ case '=':
+ vc->vc_priv = EPeq;
+ return;
+ case '<':
+ vc->vc_priv = EPlt;
+ return;
+ }
+ vc->vc_priv = EPecma;
+ fallthrough;
+ case ESgetpars: /* ESC [ aka CSI, parameters expected */
+ switch (c) {
+ case ':': /* ITU-T T.416 color subparameters */
+ if (vc->vc_par[vc->vc_npar] == CSI_m_FG_COLOR ||
+ vc->vc_par[vc->vc_npar] == CSI_m_BG_COLOR ||
+ vc->vc_par[vc->vc_npar] == CSI_m_UNDERLINE_COLOR)
+ vc->vc_state = ESgetsubpars;
+ else
+ break;
+ fallthrough;
+ case ';':
+ if (vc->vc_npar < NPAR - 1) {
+ vc->vc_npar++;
+ return;
+ }
+ break;
+ case '0' ... '9':
+ vc->vc_par[vc->vc_npar] *= 10;
+ vc->vc_par[vc->vc_npar] += c - '0';
+ return;
+ }
+ if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST) {
+ vc->vc_state = EScsiignore;
+ return;
+ }
+
+ /* parameters done, handle the control char @c */
+
+ vc->vc_state = ESnormal;
+
+ switch (vc->vc_priv) {
+ case EPdec:
+ csi_DEC(tty, vc, c);
+ return;
+ case EPecma:
+ csi_ECMA(tty, vc, c);
+ return;
+ default:
+ return;
+ }
+ case ESgetsubpars: /* ESC [ 38/48/58, subparameters expected */
+ switch (c) {
+ case ':':
+ case ';':
+ if (vc->vc_npar < NPAR - 1) {
+ vc->vc_npar++;
+ return;
+ }
+ break;
+ case '0' ... '9':
+ vc->vc_par[vc->vc_npar] *= 10;
+ vc->vc_par[vc->vc_npar] += c - '0';
+ return;
+ }
+ if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST) {
+ vc->vc_state = EScsiignore;
+ return;
+ }
+
+ /* parameters done, handle the control char @c */
+
+ vc->vc_state = ESnormal;
+
+ switch (vc->vc_priv) {
+ case EPdec:
+ csi_DEC(tty, vc, c);
+ return;
+ case EPecma:
+ csi_ECMA(tty, vc, c);
+ return;
+ default:
+ return;
+ }
+ case EScsiignore:
+ if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST)
+ return;
+ vc->vc_state = ESnormal;
+ return;
+ case ESpercent: /* ESC % */
+ 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: /* ESC [ [ aka CSI [ */
+ vc->vc_state = ESnormal;
+ return;
+ case EShash: /* ESC # */
+ 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, CSI_J_VISIBLE);
+ 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: /* ESC ( */
+ vc_setGx(vc, 0, c);
+ vc->vc_state = ESnormal;
+ return;
+ case ESsetG1: /* ESC ) */
+ vc_setGx(vc, 1, c);
+ vc->vc_state = ESnormal;
+ return;
+ case ESapc: /* ESC _ */
+ return;
+ case ESosc: /* ESC ] [0-9] aka OSC [0-9] */
+ return;
+ case ESpm: /* ESC ^ */
+ return;
+ case ESdcs: /* ESC P */
+ return;
+ default:
+ vc->vc_state = ESnormal;
+ }
+}
+
+struct vc_draw_region {
+ unsigned long from, to;
+ int x;
+};
+
+static void con_flush(struct vc_data *vc, struct vc_draw_region *draw)
+{
+ if (draw->x < 0)
+ return;
+
+ vc->vc_sw->con_putcs(vc, (u16 *)draw->from,
+ (u16 *)draw->to - (u16 *)draw->from, vc->state.y,
+ draw->x);
+ draw->x = -1;
+}
+
+static inline int vc_translate_ascii(const struct vc_data *vc, int c)
+{
+ if (IS_ENABLED(CONFIG_CONSOLE_TRANSLATIONS)) {
+ if (vc->vc_toggle_meta)
+ c |= 0x80;
+
+ return vc->vc_translate[c];
+ }
+
+ return c;
+}
+
+
+/**
+ * vc_sanitize_unicode - Replace invalid Unicode code points with ``U+FFFD``
+ * @c: the received code point
+ */
+static inline int vc_sanitize_unicode(const int c)
+{
+ if (c >= 0xd800 && c <= 0xdfff)
+ return 0xfffd;
+
+ return c;
+}
+
+/**
+ * vc_translate_unicode - Combine UTF-8 into Unicode in &vc_data.vc_utf_char
+ * @vc: virtual console
+ * @c: UTF-8 byte to translate
+ * @rescan: set to true iff @c wasn't consumed here and needs to be re-processed
+ *
+ * * &vc_data.vc_utf_char is the being-constructed Unicode code point.
+ * * &vc_data.vc_utf_count is the number of continuation bytes still expected to
+ * arrive.
+ * * &vc_data.vc_npar is the number of continuation bytes arrived so far.
+ *
+ * Return:
+ * * %-1 - Input OK so far, @c consumed, further bytes expected.
+ * * %0xFFFD - Possibility 1: input invalid, @c may have been consumed (see
+ * desc. of @rescan). Possibility 2: input OK, @c consumed,
+ * ``U+FFFD`` is the resulting code point. ``U+FFFD`` is valid,
+ * ``REPLACEMENT CHARACTER``.
+ * * otherwise - Input OK, @c consumed, resulting code point returned.
+ */
+static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan)
+{
+ static const u32 utf8_length_changes[] = {0x7f, 0x7ff, 0xffff, 0x10ffff};
+
+ /* Continuation byte received */
+ if ((c & 0xc0) == 0x80) {
+ /* Unexpected continuation byte? */
+ if (!vc->vc_utf_count)
+ goto bad_sequence;
+
+ vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
+ vc->vc_npar++;
+ if (--vc->vc_utf_count)
+ goto need_more_bytes;
+
+ /* 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])
+ goto bad_sequence;
+
+ return vc_sanitize_unicode(c);
+ }
+
+ /* Single ASCII byte or first byte of a sequence received */
+ if (vc->vc_utf_count) {
+ /* A continuation byte was expected */
+ *rescan = true;
+ vc->vc_utf_count = 0;
+ goto bad_sequence;
+ }
+
+ /* Nothing to do if an ASCII byte was received */
+ if (c <= 0x7f)
+ return c;
+
+ /* 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 {
+ goto bad_sequence;
+ }
+
+need_more_bytes:
+ return -1;
+
+bad_sequence:
+ return 0xfffd;
+}
+
+static int vc_translate(struct vc_data *vc, int *c, bool *rescan)
+{
+ /* Do no translation at all in control states */
+ if (vc->vc_state != ESnormal)
+ return *c;
+
+ if (vc->vc_utf && !vc->vc_disp_ctrl)
+ return *c = vc_translate_unicode(vc, *c, rescan);
+
+ /* no utf or alternate charset mode */
+ return vc_translate_ascii(vc, *c);
+}
+
+static inline unsigned char vc_invert_attr(const struct vc_data *vc)
+{
+ if (!vc->vc_can_do_color)
+ return vc->vc_attr ^ 0x08;
+
+ if (vc->vc_hi_font_mask == 0x100)
+ return (vc->vc_attr & 0x11) |
+ ((vc->vc_attr & 0xe0) >> 4) |
+ ((vc->vc_attr & 0x0e) << 4);
+
+ return (vc->vc_attr & 0x88) |
+ ((vc->vc_attr & 0x70) >> 4) |
+ ((vc->vc_attr & 0x07) << 4);
+}
+
+static bool vc_is_control(struct vc_data *vc, int tc, int c)
+{
+ /*
+ * 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.
+ */
+ static const u32 CTRL_ACTION = BIT(ASCII_NULL) |
+ GENMASK(ASCII_SHIFTIN, ASCII_BELL) | BIT(ASCII_CANCEL) |
+ BIT(ASCII_SUBSTITUTE) | BIT(ASCII_ESCAPE);
+ /* Cannot be overridden by disp_ctrl */
+ static const u32 CTRL_ALWAYS = BIT(ASCII_NULL) | BIT(ASCII_BACKSPACE) |
+ BIT(ASCII_LINEFEED) | BIT(ASCII_SHIFTIN) | BIT(ASCII_SHIFTOUT) |
+ BIT(ASCII_CAR_RET) | BIT(ASCII_FORMFEED) | BIT(ASCII_ESCAPE);
+
+ if (vc->vc_state != ESnormal)
+ return true;
+
+ if (!tc)
+ return true;
+
+ /*
+ * 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.
+ */
+ if (c < BITS_PER_TYPE(CTRL_ALWAYS)) {
+ if (vc->vc_disp_ctrl)
+ return CTRL_ALWAYS & BIT(c);
+ else
+ return vc->vc_utf || (CTRL_ACTION & BIT(c));
+ }
+
+ if (c == ASCII_DEL && !vc->vc_disp_ctrl)
+ return true;
+
+ if (c == ASCII_EXT_CSI)
+ return true;
+
+ return false;
+}
+
+static void vc_con_rewind(struct vc_data *vc)
+{
+ if (vc->state.x && !vc->vc_need_wrap) {
+ vc->vc_pos -= 2;
+ vc->state.x--;
+ }
+ vc->vc_need_wrap = 0;
+}
+
+#define UCS_ZWS 0x200b /* Zero Width Space */
+#define UCS_VS16 0xfe0f /* Variation Selector 16 */
+#define UCS_REPLACEMENT 0xfffd /* Replacement Character */
+
+static int vc_process_ucs(struct vc_data *vc, int *c, int *tc)
+{
+ u32 prev_c, curr_c = *c;
+ unsigned int w = ucs_get_width(curr_c);
+
+ if (likely(w == 1))
+ return 1;
+
+ if (w == 2) {
+ /*
+ * The Unicode screen memory is allocated only when
+ * required. This is one such case as we need to remember
+ * which displayed characters are double-width.
+ */
+ vc_uniscr_check(vc);
+ return 2;
+ }
+
+ /* From here curr_c is known to be zero-width. */
+
+ if (ucs_get_width(vc_uniscr_getc(vc, -2)) == 2) {
+ /*
+ * Let's merge this zero-width code point with the preceding
+ * double-width code point by replacing the existing
+ * zero-width space padding. To do so we rewind one column
+ * and pretend this has a width of 1.
+ * We give the legacy display the same initial space padding.
+ */
+ vc_con_rewind(vc);
+ *tc = ' ';
+ return 1;
+ }
+
+ /* From here the preceding character, if any, must be single-width. */
+ prev_c = vc_uniscr_getc(vc, -1);
+
+ if (curr_c == UCS_VS16 && prev_c != 0) {
+ /*
+ * VS16 (U+FE0F) is special. It typically turns the preceding
+ * single-width character into a double-width one. Let it
+ * have a width of 1 effectively making the combination with
+ * the preceding character double-width.
+ */
+ *tc = ' ';
+ return 1;
+ }
+
+ /* try recomposition */
+ prev_c = ucs_recompose(prev_c, curr_c);
+ if (prev_c != 0) {
+ vc_con_rewind(vc);
+ *tc = *c = prev_c;
+ return 1;
+ }
+
+ /* Otherwise zero-width code points are ignored. */
+ return 0;
+}
+
+static int vc_get_glyph(struct vc_data *vc, int tc)
+{
+ int glyph = conv_uni_to_pc(vc, tc);
+ u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+
+ if (!(glyph & ~charmask))
+ return glyph;
+
+ if (glyph == -1)
+ return -1; /* nothing to display */
+
+ /* Glyph not found */
+ if ((!vc->vc_utf || vc->vc_disp_ctrl || tc < 128) && !(tc & ~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.
+ */
+ return tc;
+ }
+
+ /*
+ * The Unicode screen memory is allocated only when required.
+ * This is one such case: we're about to "cheat" with the displayed
+ * character meaning the simple screen buffer won't hold the original
+ * information, whereas the Unicode screen buffer always does.
+ */
+ vc_uniscr_check(vc);
+
+ /* Try getting a simpler fallback character. */
+ tc = ucs_get_fallback(tc);
+ if (tc)
+ return vc_get_glyph(vc, tc);
+
+ /* Display U+FFFD (Unicode Replacement Character). */
+ return conv_uni_to_pc(vc, UCS_REPLACEMENT);
+}
+
+static int vc_con_write_normal(struct vc_data *vc, int tc, int c,
+ struct vc_draw_region *draw)
+{
+ int next_c;
+ unsigned char vc_attr = vc->vc_attr;
+ u16 himask = vc->vc_hi_font_mask;
+ u8 width = 1;
+ bool inverse = false;
+
+ if (vc->vc_utf && !vc->vc_disp_ctrl) {
+ width = vc_process_ucs(vc, &c, &tc);
+ if (!width)
+ goto out;
+ }
+
+ /* Now try to find out how to display it */
+ tc = vc_get_glyph(vc, tc);
+ if (tc == -1)
+ return -1; /* nothing to display */
+ if (tc < 0) {
+ inverse = true;
+ tc = conv_uni_to_pc(vc, '?');
+ if (tc < 0)
+ tc = '?';
+
+ vc_attr = vc_invert_attr(vc);
+ con_flush(vc, draw);
+ }
+
+ next_c = c;
+ while (1) {
+ if (vc->vc_need_wrap || vc->vc_decim)
+ con_flush(vc, draw);
+ if (vc->vc_need_wrap) {
+ cr(vc);
+ lf(vc);
+ }
+ if (vc->vc_decim)
+ insert_char(vc, 1);
+ vc_uniscr_putc(vc, next_c);
+
+ if (himask)
+ tc = ((tc & 0x100) ? himask : 0) |
+ (tc & 0xff);
+ tc |= (vc_attr << 8) & ~himask;
+
+ scr_writew(tc, (u16 *)vc->vc_pos);
+
+ if (con_should_update(vc) && draw->x < 0) {
+ draw->x = vc->state.x;
+ draw->from = vc->vc_pos;
+ }
+ if (vc->state.x == vc->vc_cols - 1) {
+ vc->vc_need_wrap = vc->vc_decawm;
+ draw->to = vc->vc_pos + 2;
+ } else {
+ vc->state.x++;
+ draw->to = (vc->vc_pos += 2);
+ }
+
+ if (!--width)
+ break;
+
+ /* A space is printed in the second column */
+ tc = conv_uni_to_pc(vc, ' ');
+ if (tc < 0)
+ tc = ' ';
+ /*
+ * Store a zero-width space in the Unicode screen given that
+ * the previous code point is semantically double width.
+ */
+ next_c = UCS_ZWS;
+ }
+
+out:
+ notify_write(vc, c);
+
+ if (inverse)
+ con_flush(vc, draw);
+
+ return 0;
+}
+
+/* acquires console_lock */
+static int do_con_write(struct tty_struct *tty, const u8 *buf, int count)
+{
+ struct vc_draw_region draw = {
+ .x = -1,
+ };
+ int c, tc, n = 0;
+ unsigned int currcons;
+ struct vc_data *vc = tty->driver_data;
+ struct vt_notifier_param param;
+ bool rescan;
+
+ if (in_interrupt())
+ return count;
+
+ guard(console_lock)();
+ currcons = vc->vc_num;
+ if (!vc_cons_allocated(currcons)) {
+ /* could this happen? */
+ pr_warn_once("con_write: tty %d not allocated\n", currcons+1);
+ return 0;
+ }
+
+
+ /* undraw cursor first */
+ if (con_is_fg(vc))
+ hide_cursor(vc);
+
+ param.vc = vc;
+
+ while (!tty->flow.stopped && count) {
+ u8 orig = *buf;
+ buf++;
+ n++;
+ count--;
+rescan_last_byte:
+ c = orig;
+ rescan = false;
+
+ tc = vc_translate(vc, &c, &rescan);
+ if (tc == -1)
+ continue;
+
+ param.c = tc;
+ if (atomic_notifier_call_chain(&vt_notifier_list, VT_PREWRITE,
+ &param) == NOTIFY_STOP)
+ continue;
+
+ if (vc_is_control(vc, tc, c)) {
+ con_flush(vc, &draw);
+ do_con_trol(tty, vc, orig);
+ continue;
+ }
+
+ if (vc_con_write_normal(vc, tc, c, &draw) < 0)
+ continue;
+
+ if (rescan)
+ goto rescan_last_byte;
+ }
+ con_flush(vc, &draw);
+ notify_update(vc);
+
+ return n;
+}
+
+/*
+ * 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_lock.
+ */
+static void console_callback(struct work_struct *ignored)
+{
+ guard(console_lock)();
+
+ 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->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);
+}
+
+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 start_x, cnt;
+ int kmsg_console;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ /* this protects against concurrent oops only */
+ if (!spin_trylock(&printing_lock))
+ return;
+
+ kmsg_console = vt_get_kmsg_redirect();
+ if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
+ vc = vc_cons[kmsg_console - 1].d;
+
+ if (!vc_cons_allocated(fg_console)) {
+ /* impossible */
+ /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
+ goto quit;
+ }
+
+ if (vc->vc_mode != KD_TEXT)
+ goto quit;
+
+ /* undraw cursor first */
+ if (con_is_fg(vc))
+ hide_cursor(vc);
+
+ start = (ushort *)vc->vc_pos;
+ start_x = vc->state.x;
+ cnt = 0;
+ while (count--) {
+ c = *b++;
+ if (c == ASCII_LINEFEED || c == ASCII_CAR_RET ||
+ c == ASCII_BACKSPACE || vc->vc_need_wrap) {
+ if (cnt && con_is_visible(vc))
+ vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x);
+ cnt = 0;
+ if (c == ASCII_BACKSPACE) {
+ bs(vc);
+ start = (ushort *)vc->vc_pos;
+ start_x = vc->state.x;
+ continue;
+ }
+ if (c != ASCII_CAR_RET)
+ lf(vc);
+ cr(vc);
+ start = (ushort *)vc->vc_pos;
+ start_x = vc->state.x;
+ if (c == ASCII_LINEFEED || c == ASCII_CAR_RET)
+ continue;
+ }
+ vc_uniscr_putc(vc, c);
+ scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
+ notify_write(vc, c);
+ cnt++;
+ if (vc->state.x == vc->vc_cols - 1) {
+ vc->vc_need_wrap = 1;
+ } else {
+ vc->vc_pos += 2;
+ vc->state.x++;
+ }
+ }
+ if (cnt && con_is_visible(vc))
+ vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x);
+ 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 int vt_console_setup(struct console *co, char *options)
+{
+ return co->index >= MAX_NR_CONSOLES ? -EINVAL : 0;
+}
+
+static struct console vt_console_driver = {
+ .name = "tty",
+ .setup = vt_console_setup,
+ .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_lock();.
+ *
+ * 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_user has locking, and definitely needs it
+ */
+
+int tioclinux(struct tty_struct *tty, unsigned long arg)
+{
+ char type, data;
+ char __user *p = (char __user *)arg;
+ void __user *param_aligned32 = (u32 __user *)arg + 1;
+ void __user *param = (void __user *)arg + 1;
+ 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:
+ return set_selection_user(param, tty);
+ case TIOCL_PASTESEL:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return paste_selection(tty);
+ case TIOCL_UNBLANKSCREEN:
+ scoped_guard(console_lock)
+ unblank_screen();
+ break;
+ case TIOCL_SELLOADLUT:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return sel_loadlut(param_aligned32);
+ 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 = vt_get_shift_state();
+ return put_user(data, p);
+ case TIOCL_GETMOUSEREPORTING:
+ scoped_guard(console_lock) /* May be overkill */
+ data = mouse_reporting();
+ return put_user(data, p);
+ case TIOCL_SETVESABLANK:
+ return set_vesa_blanking(param);
+ case TIOCL_GETKMSGREDIRECT:
+ data = vt_get_kmsg_redirect();
+ return put_user(data, p);
+ case TIOCL_SETKMSGREDIRECT:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(data, p+1))
+ return -EFAULT;
+
+ vt_kmsg_redirect(data);
+
+ break;
+ case TIOCL_GETFGCONSOLE:
+ /*
+ * No locking needed as this is a transiently correct return
+ * anyway if the caller hasn't disabled switching.
+ */
+ return fg_console;
+ case TIOCL_SCROLLCONSOLE:
+ if (get_user(lines, (s32 __user *)param_aligned32))
+ return -EFAULT;
+
+ /*
+ * Needs the console lock here. Note that lots of other calls
+ * need fixing before the lock is actually useful!
+ */
+ scoped_guard(console_lock)
+ scrollfront(vc_cons[fg_console].d, lines);
+ break;
+ case TIOCL_BLANKSCREEN: /* until explicitly unblanked, not only poked */
+ scoped_guard(console_lock) {
+ ignore_poke = 1;
+ do_blank_screen(0);
+ }
+ break;
+ case TIOCL_BLANKEDSCREEN:
+ return console_blanked;
+ case TIOCL_GETBRACKETEDPASTE:
+ return get_bracketed_paste(tty);
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * /dev/ttyN handling
+ */
+
+static ssize_t con_write(struct tty_struct *tty, const u8 *buf, size_t 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, u8 ch)
+{
+ return do_con_write(tty, &ch, 1);
+}
+
+static unsigned int con_write_room(struct tty_struct *tty)
+{
+ if (tty->flow.stopped)
+ return 0;
+ return 32768; /* No limit, really; 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;
+ vt_kbd_con_stop(console_num);
+}
+
+/*
+ * 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;
+ vt_kbd_con_start(console_num);
+}
+
+static void con_flush_chars(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+
+ if (in_interrupt()) /* from flush_to_ldisc */
+ return;
+
+ guard(console_lock)();
+ set_cursor(vc);
+}
+
+/*
+ * Allocate the console screen memory.
+ */
+static int con_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ unsigned int currcons = tty->index;
+ struct vc_data *vc;
+ int ret;
+
+ guard(console_lock)();
+ ret = vc_allocate(currcons);
+ if (ret)
+ return ret;
+
+ vc = vc_cons[currcons].d;
+
+ /* Still being freed */
+ if (vc->port.tty)
+ return -ERESTARTSYS;
+
+ ret = tty_port_install(&vc->port, driver, tty);
+ if (ret)
+ return ret;
+
+ tty->driver_data = vc;
+ vc->port.tty = tty;
+ tty_port_get(&vc->port);
+
+ 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;
+
+ return 0;
+}
+
+static int con_open(struct tty_struct *tty, struct file *filp)
+{
+ /* everything done in install */
+ return 0;
+}
+
+
+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);
+
+ guard(console_lock)();
+ vc->port.tty = NULL;
+}
+
+static void con_cleanup(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+
+ tty_port_put(&vc->port);
+}
+
+/*
+ * We can't deal with anything but the N_TTY ldisc,
+ * because we can sleep in our write() routine.
+ */
+static int con_ldisc_ok(struct tty_struct *tty, int ldisc)
+{
+ return ldisc == N_TTY ? 0 : -EINVAL;
+}
+
+static int default_color = 7; /* white */
+static int default_italic_color = 2; // green (ASCII)
+static int default_underline_color = 3; // cyan (ASCII)
+module_param_named(color, default_color, int, S_IRUGO | S_IWUSR);
+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, int do_clear)
+{
+ int j, k ;
+
+ 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 = default_color;
+ 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;
+
+ console_lock();
+
+ if (!conswitchp)
+ conswitchp = &dummy_con;
+ display_desc = conswitchp->con_startup();
+ if (!display_desc) {
+ fg_console = 0;
+ console_unlock();
+ return 0;
+ }
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = conswitchp;
+ con_driver->desc = display_desc;
+ con_driver->flag = CON_DRIVER_FLAG_INIT;
+ con_driver->first = 0;
+ con_driver->last = MAX_NR_CONSOLES - 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ con_driver_map[i] = conswitchp;
+
+ if (blankinterval) {
+ blank_state = blank_normal_wait;
+ mod_timer(&console_timer, jiffies + (blankinterval * HZ));
+ }
+
+ for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
+ vc_cons[currcons].d = vc = kzalloc_obj(struct vc_data,
+ GFP_NOWAIT);
+ INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+ tty_port_init(&vc->port);
+ visual_init(vc, currcons, true);
+ /* Assuming vc->vc_{cols,rows,screenbuf_size} are sane here. */
+ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
+ vc_init(vc, 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->state.x, vc->state.y);
+ csi_J(vc, CSI_J_CURSOR_TO_END);
+ update_screen(vc);
+ pr_info("Console: %s %s %dx%d\n",
+ vc->vc_can_do_color ? "colour" : "mono",
+ display_desc, vc->vc_cols, vc->vc_rows);
+
+ console_unlock();
+
+#ifdef CONFIG_VT_CONSOLE
+ register_console(&vt_console_driver);
+#endif
+ return 0;
+}
+console_initcall(con_init);
+
+static const struct tty_operations con_ops = {
+ .install = con_install,
+ .open = con_open,
+ .close = con_close,
+ .write = con_write,
+ .write_room = con_write_room,
+ .put_char = con_put_char,
+ .flush_chars = con_flush_chars,
+ .ioctl = vt_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vt_compat_ioctl,
+#endif
+ .stop = con_stop,
+ .start = con_start,
+ .throttle = con_throttle,
+ .unthrottle = con_unthrottle,
+ .resize = vt_resize,
+ .shutdown = con_shutdown,
+ .cleanup = con_cleanup,
+ .ldisc_ok = con_ldisc_ok,
+};
+
+static struct cdev vc0_cdev;
+
+static ssize_t show_tty_active(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "tty%d\n", fg_console + 1);
+}
+static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);
+
+static struct attribute *vt_dev_attrs[] = {
+ &dev_attr_active.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(vt_dev);
+
+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");
+ tty0dev = device_create_with_groups(&tty_class, NULL,
+ MKDEV(TTY_MAJOR, 0), NULL,
+ vt_dev_groups, "tty0");
+ if (IS_ERR(tty0dev))
+ tty0dev = NULL;
+
+ vcs_init();
+
+ console_driver = tty_alloc_driver(MAX_NR_CONSOLES, TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_RESET_TERMIOS);
+ if (IS_ERR(console_driver))
+ panic("Couldn't allocate console driver\n");
+
+ 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;
+ 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();
+ return 0;
+}
+
+static const struct class vtconsole_class = {
+ .name = "vtconsole",
+};
+
+static int do_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;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ /* check if driver is registered */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_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, false);
+ 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);
+ }
+
+ pr_info("Console: switching ");
+ if (!deflt)
+ pr_cont("consoles %d-%d ", first + 1, last + 1);
+ if (j >= 0) {
+ struct vc_data *vc = vc_cons[j].d;
+
+ pr_cont("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 {
+ pr_cont("to %s\n", desc);
+ }
+
+ retval = 0;
+err:
+ module_put(owner);
+ return retval;
+};
+
+
+#ifdef CONFIG_VT_HW_CONSOLE_BINDING
+int do_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;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ /* check if driver is registered and if it is unbindable */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_MODULE) {
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ retval = -ENODEV;
+
+ /* check if backup driver exists */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_back = &registered_con_driver[i];
+
+ if (con_back->con && con_back->con != csw) {
+ defcsw = con_back->con;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ if (!con_is_bound(csw))
+ 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;
+
+ /* ignore return value, binding should not fail */
+ do_bind_con_driver(defcsw, first, last, deflt);
+err:
+ module_put(owner);
+ return retval;
+
+}
+EXPORT_SYMBOL_GPL(do_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))
+ goto err;
+
+ csw = con->con;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con = &registered_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)
+ do_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;
+ int ret;
+
+ if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE))
+ 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) {
+ ret = do_unbind_con_driver(csw, first, last, deflt);
+ if (ret != 0)
+ return ret;
+ }
+
+ 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);
+
+ guard(console_lock)();
+
+ 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;
+
+ scoped_guard(console_lock)
+ bind = con_is_bound(con->con);
+
+ return sysfs_emit(buf, "%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 sysfs_emit(buf, "%s %s\n",
+ (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)",
+ con->desc);
+
+}
+
+static DEVICE_ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *con_dev_attrs[] = {
+ &dev_attr_bind.attr,
+ &dev_attr_name.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(con_dev);
+
+static int vtconsole_init_device(struct con_driver *con)
+{
+ con->flag |= CON_DRIVER_FLAG_ATTR;
+ return 0;
+}
+
+static void vtconsole_deinit_device(struct con_driver *con)
+{
+ 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 &consw.con_startup()
+ */
+int con_is_bound(const struct consw *csw)
+{
+ int i, bound = 0;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ 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_is_visible - checks whether the current console is visible
+ * @vc: virtual console
+ *
+ * RETURNS: zero if not visible, nonzero if visible
+ */
+bool con_is_visible(const struct vc_data *vc)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ return *vc->vc_display_fg == vc;
+}
+EXPORT_SYMBOL(con_is_visible);
+
+/**
+ * con_debug_enter - prepare the console for the kernel debugger
+ * @vc: virtual console
+ *
+ * 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.
+ */
+void con_debug_enter(struct vc_data *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);
+ }
+ }
+ if (vc->vc_cols < 999) {
+ int colcount;
+ char cols[4];
+ const char *setargs[3] = {
+ "set",
+ "COLUMNS",
+ cols,
+ };
+ if (kdbgetintenv(setargs[0], &colcount)) {
+ snprintf(cols, 4, "%i", vc->vc_cols);
+ kdb_set(2, setargs);
+ }
+ }
+#endif /* CONFIG_KGDB_KDB */
+}
+EXPORT_SYMBOL_GPL(con_debug_enter);
+
+/**
+ * con_debug_leave - restore console state
+ *
+ * Restore the console state to what it was before the kernel debugger
+ * was invoked.
+ */
+void con_debug_leave(void)
+{ }
+EXPORT_SYMBOL_GPL(con_debug_leave);
+
+static int do_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;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ /* already registered */
+ if (con_driver->con == csw) {
+ retval = -EBUSY;
+ goto err;
+ }
+ }
+
+ desc = csw->con_startup();
+ if (!desc) {
+ retval = -ENODEV;
+ goto err;
+ }
+
+ retval = -EINVAL;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == NULL &&
+ !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) {
+ 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_with_groups(&vtconsole_class, NULL,
+ MKDEV(0, con_driver->node),
+ con_driver, con_dev_groups,
+ "vtcon%i", con_driver->node);
+ if (IS_ERR(con_driver->dev)) {
+ pr_warn("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:
+ module_put(owner);
+ return retval;
+}
+
+
+/**
+ * do_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 do_unregister_con_driver(const struct consw *csw)
+{
+ int i;
+
+ /* cannot unregister a bound driver */
+ if (con_is_bound(csw))
+ return -EBUSY;
+
+ if (csw == conswitchp)
+ return -EINVAL;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw) {
+ /*
+ * Defer the removal of the sysfs entries since that
+ * will acquire the kernfs s_active lock and we can't
+ * acquire this lock while holding the console lock:
+ * the unbind sysfs entry imposes already the opposite
+ * order. Reset con already here to prevent any later
+ * lookup to succeed and mark this slot as zombie, so
+ * it won't get reused until we complete the removal
+ * in the deferred work.
+ */
+ con_driver->con = NULL;
+ con_driver->flag = CON_DRIVER_FLAG_ZOMBIE;
+ schedule_work(&con_driver_unregister_work);
+
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(do_unregister_con_driver);
+
+static void con_driver_unregister_callback(struct work_struct *ignored)
+{
+ int i;
+
+ guard(console_lock)();
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (!(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE))
+ continue;
+
+ console_unlock();
+
+ vtconsole_deinit_device(con_driver);
+ device_destroy(&vtconsole_class, MKDEV(0, con_driver->node));
+
+ console_lock();
+
+ if (WARN_ON_ONCE(con_driver->con))
+ con_driver->con = NULL;
+ con_driver->desc = NULL;
+ con_driver->dev = NULL;
+ con_driver->node = 0;
+ WARN_ON_ONCE(con_driver->flag != CON_DRIVER_FLAG_ZOMBIE);
+ con_driver->flag = 0;
+ con_driver->first = 0;
+ con_driver->last = 0;
+ }
+}
+
+/*
+ * 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.
+ *
+ * do_take_over_console is basically a register followed by bind
+ */
+int do_take_over_console(const struct consw *csw, int first, int last, int deflt)
+{
+ int err;
+
+ err = do_register_con_driver(csw, first, last);
+ /*
+ * If we get an busy error we still want to bind the console driver
+ * and return success, as we may have unbound the console driver
+ * but not unregistered it.
+ */
+ if (err == -EBUSY)
+ err = 0;
+ if (!err)
+ do_bind_con_driver(csw, first, last, deflt);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(do_take_over_console);
+
+
+/*
+ * 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)
+{
+ guard(console_lock)();
+ do_unregister_con_driver(csw);
+}
+EXPORT_SYMBOL(give_up_console);
+
+static int __init vtconsole_class_init(void)
+{
+ int i;
+
+ i = class_register(&vtconsole_class);
+ if (i)
+ pr_warn("Unable to create vt console class; errno = %d\n", i);
+
+ /* Add system drivers to sysfs */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con = &registered_con_driver[i];
+
+ if (con->con && !con->dev) {
+ con->dev =
+ device_create_with_groups(&vtconsole_class, NULL,
+ MKDEV(0, con->node),
+ con, con_dev_groups,
+ "vtcon%i", con->node);
+
+ if (IS_ERR(con->dev)) {
+ pr_warn("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);
+
+/*
+ * Screen blanking
+ */
+
+static int set_vesa_blanking(u8 __user *mode_user)
+{
+ u8 mode;
+
+ if (get_user(mode, mode_user))
+ return -EFAULT;
+
+ guard(console_lock)();
+ vesa_blank_mode = (mode <= VESA_BLANK_MAX) ? mode : VESA_NO_BLANKING;
+
+ return 0;
+}
+
+void do_blank_screen(int entering_gfx)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+ int i;
+
+ might_sleep();
+
+ 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, VESA_VSYNC_SUSPEND, 1);
+ console_blanked = fg_console + 1;
+ blank_state = blank_off;
+ set_origin(vc);
+ return;
+ }
+
+ blank_state = blank_off;
+
+ /* don't blank graphics */
+ if (vc->vc_mode != KD_TEXT) {
+ console_blanked = fg_console + 1;
+ return;
+ }
+
+ hide_cursor(vc);
+ timer_delete_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 ? VESA_VSYNC_SUSPEND :
+ (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 */
+ pr_warn("unblank_screen: tty %d not allocated ??\n",
+ fg_console + 1);
+ return;
+ }
+ vc = vc_cons[fg_console].d;
+ if (vc->vc_mode != KD_TEXT)
+ 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, VESA_NO_BLANKING, leaving_gfx))
+ /* 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);
+ notify_update(vc);
+}
+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...
+ */
+static 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(struct timer_list *unused)
+{
+ 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 worst, we'll do a spurious blank and it's unlikely
+ */
+ timer_delete(&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->vc_sw->con_set_palette(vc, color_table);
+}
+
+/*
+ * 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 i, j, k;
+ unsigned char colormap[3*16];
+
+ if (copy_from_user(colormap, arg, sizeof(colormap)))
+ return -EFAULT;
+
+ guard(console_lock)();
+ for (i = k = 0; i < 16; i++) {
+ default_red[i] = colormap[k++];
+ default_grn[i] = colormap[k++];
+ default_blu[i] = colormap[k++];
+ }
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!vc_cons_allocated(i))
+ continue;
+ 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;
+}
+
+int con_get_cmap(unsigned char __user *arg)
+{
+ int i, k;
+ unsigned char colormap[3*16];
+
+ scoped_guard(console_lock)
+ for (i = k = 0; i < 16; i++) {
+ colormap[k++] = default_red[i];
+ colormap[k++] = default_grn[i];
+ colormap[k++] = default_blu[i];
+ }
+
+ if (copy_to_user(arg, colormap, sizeof(colormap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+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 128 pixels wide, at a maximum height
+ * of 128 pixels. Userspace fontdata may have to be 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 up to 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_width 64
+#define max_font_height 128
+#define max_font_glyphs 512
+#define max_font_size (max_font_glyphs*max_font_width*max_font_height)
+
+static int con_font_get(struct vc_data *vc, struct console_font_op *op)
+{
+ struct console_font font;
+ int c;
+ unsigned int vpitch = op->op == KD_FONT_OP_GET_TALL ? op->height : 32;
+
+ if (vpitch > max_font_height)
+ return -EINVAL;
+
+ void *font_data __free(kvfree) = NULL;
+ if (op->data) {
+ font.data = font_data = kvzalloc(max_font_size, GFP_KERNEL);
+ if (!font.data)
+ return -ENOMEM;
+ } else
+ font.data = NULL;
+
+ scoped_guard(console_lock) {
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+ if (!vc->vc_sw->con_font_get)
+ return -ENOSYS;
+
+ int ret = vc->vc_sw->con_font_get(vc, &font, vpitch);
+ if (ret)
+ return ret;
+ }
+
+ c = DIV_ROUND_UP(font.width, 8) * vpitch * font.charcount;
+
+ if (op->data && font.charcount > op->charcount)
+ return -ENOSPC;
+ if (font.width > op->width || font.height > op->height)
+ return -ENOSPC;
+
+ op->height = font.height;
+ op->width = font.width;
+ op->charcount = font.charcount;
+
+ if (op->data && copy_to_user(op->data, font.data, c))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int con_font_set(struct vc_data *vc, const struct console_font_op *op)
+{
+ struct console_font font;
+ int size;
+ unsigned int vpitch = op->op == KD_FONT_OP_SET_TALL ? op->height : 32;
+
+ if (!op->data)
+ return -EINVAL;
+ if (op->charcount > max_font_glyphs)
+ return -EINVAL;
+ if (op->width <= 0 || op->width > max_font_width || !op->height ||
+ op->height > max_font_height)
+ return -EINVAL;
+ if (vpitch < op->height)
+ return -EINVAL;
+ size = DIV_ROUND_UP(op->width, 8) * vpitch * op->charcount;
+ if (size > max_font_size)
+ return -ENOSPC;
+
+ void *font_data __free(kfree) = font.data = memdup_user(op->data, size);
+ if (IS_ERR(font.data))
+ return PTR_ERR(font.data);
+
+ font.charcount = op->charcount;
+ font.width = op->width;
+ font.height = op->height;
+
+ guard(console_lock)();
+
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+ if (!vc->vc_sw->con_font_set)
+ return -ENOSYS;
+
+ /* hide selection and cursor prior font changes */
+ hide_cursor(vc);
+
+ return vc->vc_sw->con_font_set(vc, &font, vpitch, op->flags);
+}
+
+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;
+
+ 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;
+
+ scoped_guard(console_lock) {
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+ if (!vc->vc_sw->con_font_default)
+ return -ENOSYS;
+
+ /* hide selection and cursor prior font changes */
+ hide_cursor(vc);
+
+ int ret = vc->vc_sw->con_font_default(vc, &font, s);
+ if (ret)
+ return ret;
+ }
+
+ op->width = font.width;
+ op->height = font.height;
+
+ return 0;
+}
+
+int con_font_op(struct vc_data *vc, struct console_font_op *op)
+{
+ switch (op->op) {
+ case KD_FONT_OP_SET:
+ case KD_FONT_OP_SET_TALL:
+ return con_font_set(vc, op);
+ case KD_FONT_OP_GET:
+ case KD_FONT_OP_GET_TALL:
+ return con_font_get(vc, op);
+ case KD_FONT_OP_SET_DEFAULT:
+ return con_font_default(vc, op);
+ case KD_FONT_OP_COPY:
+ /* was buggy and never really used */
+ return -EINVAL;
+ }
+ return -ENOSYS;
+}
+
+/*
+ * Interface exported to selection and vcs.
+ */
+
+/* used by selection */
+u16 screen_glyph(const struct vc_data *vc, int offset)
+{
+ u16 w = scr_readw(screenpos(vc, offset, true));
+ u16 c = w & 0xff;
+
+ if (w & vc->vc_hi_font_mask)
+ c |= 0x100;
+ return c;
+}
+EXPORT_SYMBOL_GPL(screen_glyph);
+
+u32 screen_glyph_unicode(const struct vc_data *vc, int n)
+{
+ u32 **uni_lines = vc->vc_uni_lines;
+
+ if (uni_lines)
+ return uni_lines[n / vc->vc_cols][n % vc->vc_cols];
+
+ return inverse_translate(vc, screen_glyph(vc, n * 2), true);
+}
+EXPORT_SYMBOL_GPL(screen_glyph_unicode);
+
+/* used by vcs - note the word offset */
+unsigned short *screen_pos(const struct vc_data *vc, int w_offset, bool viewed)
+{
+ return screenpos(vc, 2 * w_offset, viewed);
+}
+EXPORT_SYMBOL_GPL(screen_pos);
+
+void getconsxy(const struct vc_data *vc, unsigned char xy[static 2])
+{
+ /* clamp values if they don't fit */
+ xy[0] = min(vc->state.x, 0xFFu);
+ xy[1] = min(vc->state.y, 0xFFu);
+}
+
+void putconsxy(struct vc_data *vc, unsigned char xy[static const 2])
+{
+ hide_cursor(vc);
+ gotoxy(vc, xy[0], xy[1]);
+ set_cursor(vc);
+}
+
+u16 vcs_scr_readw(const 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);
+}
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
new file mode 100644
index 000000000..ecefe4a66
--- /dev/null
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -0,0 +1,1319 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 1992 obz under the linux copyright
+ *
+ * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
+ * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
+ * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
+ * Some code moved for less code duplication - Andi Kleen - Mar 1997
+ * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched/signal.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/console.h>
+#include <linux/consolemap.h>
+#include <linux/signal.h>
+#include <linux/suspend.h>
+#include <linux/timex.h>
+
+#include <asm/io.h>
+#include <linux/uaccess.h>
+
+#include <linux/nospec.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/kbd_diacr.h>
+#include <linux/selection.h>
+
+bool vt_dont_switch;
+
+static inline bool vt_in_use(unsigned int i)
+{
+ const struct vc_data *vc = vc_cons[i].d;
+
+ /*
+ * console_lock must be held to prevent the vc from being deallocated
+ * while we're checking whether it's in-use.
+ */
+ WARN_CONSOLE_UNLOCKED();
+
+ return vc && kref_read(&vc->port.kref) > 1;
+}
+
+static inline bool vt_busy(int i)
+{
+ if (vt_in_use(i))
+ return true;
+ if (i == fg_console)
+ return true;
+ if (vc_is_sel(vc_cons[i].d))
+ return true;
+
+ return false;
+}
+
+/*
+ * 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 <asm/syscalls.h>
+#endif
+
+static void complete_change_console(struct vc_data *vc);
+
+/*
+ * User space VT_EVENT handlers
+ */
+
+struct vt_event_wait {
+ struct list_head list;
+ struct vt_event event;
+ int done;
+};
+
+static LIST_HEAD(vt_events);
+static DEFINE_SPINLOCK(vt_event_lock);
+static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue);
+
+/**
+ * vt_event_post
+ * @event: the event that occurred
+ * @old: old console
+ * @new: new console
+ *
+ * Post an VT event to interested VT handlers
+ */
+
+void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
+{
+ struct list_head *pos, *head;
+ unsigned long flags;
+ int wake = 0;
+
+ spin_lock_irqsave(&vt_event_lock, flags);
+ head = &vt_events;
+
+ list_for_each(pos, head) {
+ struct vt_event_wait *ve = list_entry(pos,
+ struct vt_event_wait, list);
+ if (!(ve->event.event & event))
+ continue;
+ ve->event.event = event;
+ /* kernel view is consoles 0..n-1, user space view is
+ console 1..n with 0 meaning current, so we must bias */
+ ve->event.oldev = old + 1;
+ ve->event.newev = new + 1;
+ wake = 1;
+ ve->done = 1;
+ }
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+ if (wake)
+ wake_up_interruptible(&vt_event_waitqueue);
+}
+
+static void __vt_event_queue(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);
+}
+
+static void __vt_event_wait(struct vt_event_wait *vw)
+{
+ /* Wait for it to pass */
+ wait_event_interruptible(vt_event_waitqueue, vw->done);
+}
+
+static void __vt_event_dequeue(struct vt_event_wait *vw)
+{
+ unsigned long flags;
+
+ /* Dequeue it */
+ spin_lock_irqsave(&vt_event_lock, flags);
+ list_del(&vw->list);
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
+/**
+ * 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)
+{
+ __vt_event_queue(vw);
+ __vt_event_wait(vw);
+ __vt_event_dequeue(vw);
+}
+
+/**
+ * vt_event_wait_ioctl - event ioctl handler
+ * @event: argument to ioctl (the event)
+ *
+ * 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
+ * @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 {
+ vw.event.event = VT_EVENT_SWITCH;
+ __vt_event_queue(&vw);
+ if (n == fg_console + 1) {
+ __vt_event_dequeue(&vw);
+ break;
+ }
+ __vt_event_wait(&vw);
+ __vt_event_dequeue(&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)
+
+/*
+ * 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
+ *
+ * Called with the console lock held.
+ */
+static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
+{
+ switch (mode) {
+ case KD_GRAPHICS:
+ break;
+ case KD_TEXT0:
+ case KD_TEXT1:
+ mode = KD_TEXT;
+ fallthrough;
+ case KD_TEXT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (vc->vc_mode == mode)
+ return 0;
+
+ vc->vc_mode = mode;
+ if (vc->vc_num != fg_console)
+ return 0;
+
+ /* explicitly blank/unblank the screen if switching modes */
+ if (mode == KD_TEXT)
+ do_unblank_screen(1);
+ else
+ do_blank_screen(1);
+
+ return 0;
+}
+
+static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg, bool perm)
+{
+ struct vc_data *vc = tty->driver_data;
+ void __user *up = (void __user *)arg;
+ unsigned int console = vc->vc_num;
+ int ret;
+
+ switch (cmd) {
+ case KIOCSOUND:
+ if (!perm)
+ return -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. kd_mksound is locked by the input layer.
+ */
+ if (arg)
+ arg = PIT_TICK_RATE / arg;
+ kd_mksound(arg, 0);
+ break;
+
+ case KDMKTONE:
+ if (!perm)
+ return -EPERM;
+ {
+ unsigned int ticks, count;
+
+ /*
+ * Generate the tone for the appropriate number of ticks.
+ * If the time is zero, turn off sound ourselves.
+ */
+ ticks = msecs_to_jiffies((arg >> 16) & 0xffff);
+ count = ticks ? (arg & 0xffff) : 0;
+ if (count)
+ count = PIT_TICK_RATE / count;
+ kd_mksound(count, ticks);
+ break;
+ }
+
+ case KDGKBTYPE:
+ /*
+ * this is naïve.
+ */
+ return put_user(KB_101, (char __user *)arg);
+
+ /*
+ * 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...
+ *
+ * These are locked internally via sys_ioperm
+ */
+ if (arg < GPFIRST || arg > GPLAST)
+ return -EINVAL;
+
+ return ksys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
+
+ case KDENABIO:
+ case KDDISABIO:
+ return ksys_ioperm(GPFIRST, GPNUM,
+ (cmd == KDENABIO)) ? -ENXIO : 0;
+#endif
+
+ /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */
+
+ case KDKBDREP:
+ {
+ struct kbd_repeat kbrep;
+
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ return -EPERM;
+
+ if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat)))
+ return -EFAULT;
+
+ ret = kbd_rate(&kbrep);
+ if (ret)
+ return ret;
+ if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat)))
+ return -EFAULT;
+ break;
+ }
+
+ case KDSETMODE: {
+ if (!perm)
+ return -EPERM;
+
+ guard(console_lock)();
+ return vt_kdsetmode(vc, arg);
+ }
+ case KDGETMODE:
+ return put_user(vc->vc_mode, (int __user *)arg);
+
+ case KDMAPDISP:
+ case KDUNMAPDISP:
+ /*
+ * these work like a combination of mmap and KDENABIO.
+ * this could be easily finished.
+ */
+ return -EINVAL;
+
+ case KDSKBMODE:
+ if (!perm)
+ return -EPERM;
+ ret = vt_do_kdskbmode(console, arg);
+ if (ret)
+ return ret;
+ tty_ldisc_flush(tty);
+ break;
+
+ case KDGKBMODE:
+ return put_user(vt_do_kdgkbmode(console), (int __user *)arg);
+
+ /* this could be folded into KDSKBMODE, but for compatibility
+ reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
+ case KDSKBMETA:
+ if (!perm)
+ return -EPERM;
+ return vt_do_kdskbmeta(console, arg);
+
+ case KDGKBMETA:
+ /* FIXME: should review whether this is worth locking */
+ return put_user(vt_do_kdgkbmeta(console), (int __user *)arg);
+
+ case KDGETKEYCODE:
+ case KDSETKEYCODE:
+ if(!capable(CAP_SYS_TTY_CONFIG))
+ perm = 0;
+ return vt_do_kbkeycode_ioctl(cmd, up, perm);
+
+ case KDGKBENT:
+ case KDSKBENT:
+ return vt_do_kdsk_ioctl(cmd, up, perm, console);
+
+ case KDGKBSENT:
+ case KDSKBSENT:
+ return vt_do_kdgkb_ioctl(cmd, up, perm);
+
+ /* Diacritical processing. Handled in keyboard.c as it has
+ to operate on the keyboard locks and structures */
+ case KDGKBDIACR:
+ case KDGKBDIACRUC:
+ case KDSKBDIACR:
+ case KDSKBDIACRUC:
+ return vt_do_diacrit(cmd, up, perm);
+
+ /* the ioctls below read/set the flags usually shown in the leds */
+ /* don't use them - they will go away without warning */
+ case KDGKBLED:
+ case KDSKBLED:
+ case KDGETLED:
+ case KDSETLED:
+ return vt_do_kdskled(console, cmd, arg, perm);
+
+ /*
+ * 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))
+ return -EPERM;
+ if (!valid_signal(arg) || arg < 1 || arg == SIGKILL)
+ return -EINVAL;
+
+ 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 KDFONTOP: {
+ struct console_font_op op;
+
+ if (copy_from_user(&op, up, sizeof(op)))
+ return -EFAULT;
+ if (!perm && op.op != KD_FONT_OP_GET)
+ return -EPERM;
+ ret = con_font_op(vc, &op);
+ if (ret)
+ return ret;
+ if (copy_to_user(up, &op, sizeof(op)))
+ return -EFAULT;
+ break;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud,
+ bool perm, struct vc_data *vc)
+{
+ struct unimapdesc tmp;
+
+ if (copy_from_user(&tmp, user_ud, sizeof tmp))
+ return -EFAULT;
+ switch (cmd) {
+ case PIO_UNIMAP:
+ if (!perm)
+ return -EPERM;
+ return con_set_unimap(vc, tmp.entry_ct, tmp.entries);
+ case GIO_UNIMAP:
+ if (!perm && fg_console != vc->vc_num)
+ return -EPERM;
+ return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct),
+ tmp.entries);
+ }
+ return 0;
+}
+
+static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up,
+ bool perm)
+{
+ switch (cmd) {
+ case PIO_CMAP:
+ if (!perm)
+ return -EPERM;
+ return con_set_cmap(up);
+
+ case GIO_CMAP:
+ return con_get_cmap(up);
+
+ case PIO_SCRNMAP:
+ if (!perm)
+ return -EPERM;
+ return con_set_trans_old(up);
+
+ case GIO_SCRNMAP:
+ return con_get_trans_old(up);
+
+ case PIO_UNISCRNMAP:
+ if (!perm)
+ return -EPERM;
+ return con_set_trans_new(up);
+
+ case GIO_UNISCRNMAP:
+ return con_get_trans_new(up);
+
+ case PIO_UNIMAPCLR:
+ if (!perm)
+ return -EPERM;
+ con_clear_unimap(vc);
+ break;
+
+ case PIO_UNIMAP:
+ case GIO_UNIMAP:
+ return do_unimap_ioctl(cmd, up, perm, vc);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int vt_reldisp(struct vc_data *vc, unsigned int swtch)
+{
+ int newvt, ret;
+
+ if (vc->vt_mode.mode != VT_PROCESS)
+ return -EINVAL;
+
+ /* Switched-to response */
+ if (vc->vt_newvt < 0) {
+ /* If it's just an ACK, ignore it */
+ return swtch == VT_ACKACQ ? 0 : -EINVAL;
+ }
+
+ /* Switching-from response */
+ if (swtch == 0) {
+ /* Switch disallowed, so forget we were trying to do it. */
+ vc->vt_newvt = -1;
+ return 0;
+ }
+
+ /* The current vt has been released, so complete the switch. */
+ newvt = vc->vt_newvt;
+ vc->vt_newvt = -1;
+ ret = vc_allocate(newvt);
+ if (ret)
+ return ret;
+
+ /*
+ * 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);
+
+ return 0;
+}
+
+static int vt_setactivate(struct vt_setactivate __user *sa)
+{
+ struct vt_setactivate vsa;
+ struct vc_data *nvc;
+ int ret;
+
+ if (copy_from_user(&vsa, sa, sizeof(vsa)))
+ return -EFAULT;
+ if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES)
+ return -ENXIO;
+ if (vsa.mode.mode != VT_AUTO && vsa.mode.mode != VT_PROCESS)
+ return -EINVAL;
+
+ vsa.console--;
+ vsa.console = array_index_nospec(vsa.console, MAX_NR_CONSOLES);
+ scoped_guard(console_lock) {
+ ret = vc_allocate(vsa.console);
+ if (ret)
+ return ret;
+
+ /*
+ * 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));
+ }
+
+ /* Commence switch and lock */
+ /* Review set_console locks */
+ set_console(vsa.console);
+
+ return 0;
+}
+
+/* deallocate a single console, if possible (leave 0) */
+static int vt_disallocate(unsigned int vc_num)
+{
+ struct vc_data *vc = NULL;
+
+ scoped_guard(console_lock) {
+ if (vt_busy(vc_num))
+ return -EBUSY;
+ if (vc_num)
+ vc = vc_deallocate(vc_num);
+ }
+
+ if (vc && vc_num >= MIN_NR_CONSOLES)
+ tty_port_put(&vc->port);
+
+ return 0;
+}
+
+/* deallocate all unused consoles, but leave 0 */
+static void vt_disallocate_all(void)
+{
+ struct vc_data *vc[MAX_NR_CONSOLES];
+ int i;
+
+ scoped_guard(console_lock)
+ for (i = 1; i < MAX_NR_CONSOLES; i++)
+ if (!vt_busy(i))
+ vc[i] = vc_deallocate(i);
+ else
+ vc[i] = NULL;
+
+ for (i = 1; i < MAX_NR_CONSOLES; i++) {
+ if (vc[i] && i >= MIN_NR_CONSOLES)
+ tty_port_put(&vc[i]->port);
+ }
+}
+
+static int vt_resizex(struct vc_data *vc, struct vt_consize __user *cs)
+{
+ struct vt_consize v;
+ int i;
+
+ if (copy_from_user(&v, cs, sizeof(struct vt_consize)))
+ return -EFAULT;
+
+ /* FIXME: Should check the copies properly */
+ if (!v.v_vlin)
+ v.v_vlin = vc->vc_scan_lines;
+
+ if (v.v_clin) {
+ int rows = v.v_vlin / v.v_clin;
+ if (v.v_rows != rows) {
+ if (v.v_rows) /* Parameters don't add up */
+ return -EINVAL;
+ v.v_rows = rows;
+ }
+ }
+
+ if (v.v_vcol && v.v_ccol) {
+ int cols = v.v_vcol / v.v_ccol;
+ if (v.v_cols != cols) {
+ if (v.v_cols)
+ return -EINVAL;
+ v.v_cols = cols;
+ }
+ }
+
+ if (v.v_clin > 32)
+ return -EINVAL;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ struct vc_data *vcp;
+
+ if (!vc_cons[i].d)
+ continue;
+ guard(console_lock)();
+ vcp = vc_cons[i].d;
+ if (vcp) {
+ int ret;
+ int save_scan_lines = vcp->vc_scan_lines;
+ int save_cell_height = vcp->vc_cell_height;
+
+ if (v.v_vlin)
+ vcp->vc_scan_lines = v.v_vlin;
+ if (v.v_clin)
+ vcp->vc_cell_height = v.v_clin;
+ ret = __vc_resize(vcp, v.v_cols, v.v_rows, true);
+ if (ret) {
+ vcp->vc_scan_lines = save_scan_lines;
+ vcp->vc_cell_height = save_cell_height;
+ return ret;
+ }
+ }
+ }
+
+ 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,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vc_data *vc = tty->driver_data;
+ void __user *up = (void __user *)arg;
+ int i, perm;
+ int ret;
+
+ /*
+ * 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;
+
+ ret = vt_k_ioctl(tty, cmd, arg, perm);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ ret = vt_io_ioctl(vc, cmd, up, perm);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ switch (cmd) {
+ case TIOCLINUX:
+ return tioclinux(tty, arg);
+ case VT_SETMODE:
+ {
+ struct vt_mode tmp;
+
+ if (!perm)
+ return -EPERM;
+ if (copy_from_user(&tmp, up, sizeof(struct vt_mode)))
+ return -EFAULT;
+ if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS)
+ return -EINVAL;
+
+ guard(console_lock)();
+ 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;
+ break;
+ }
+
+ case VT_GETMODE:
+ {
+ struct vt_mode tmp;
+ int rc;
+
+ scoped_guard(console_lock)
+ memcpy(&tmp, &vc->vt_mode, sizeof(struct vt_mode));
+
+ rc = copy_to_user(up, &tmp, sizeof(struct vt_mode));
+ if (rc)
+ return -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))
+ return -EFAULT;
+
+ state = 1; /* /dev/tty0 is always open */
+ scoped_guard(console_lock) /* required by vt_in_use() */
+ for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
+ if (vt_in_use(i))
+ state |= mask;
+ return put_user(state, &vtstat->v_state);
+ }
+
+ /*
+ * Returns the first available (non-opened) console.
+ */
+ case VT_OPENQRY:
+ scoped_guard(console_lock) /* required by vt_in_use() */
+ for (i = 0; i < MAX_NR_CONSOLES; ++i)
+ if (!vt_in_use(i))
+ break;
+ i = i < MAX_NR_CONSOLES ? (i+1) : -1;
+ return put_user(i, (int __user *)arg);
+
+ /*
+ * 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)
+ return -EPERM;
+ if (arg == 0 || arg > MAX_NR_CONSOLES)
+ return -ENXIO;
+
+ arg--;
+ arg = array_index_nospec(arg, MAX_NR_CONSOLES);
+ scoped_guard(console_lock) {
+ ret = vc_allocate(arg);
+ if (ret)
+ return ret;
+ }
+ set_console(arg);
+ break;
+
+ case VT_SETACTIVATE:
+ if (!perm)
+ return -EPERM;
+
+ return vt_setactivate(up);
+
+ /*
+ * wait until the specified VT has been activated
+ */
+ case VT_WAITACTIVE:
+ if (!perm)
+ return -EPERM;
+ if (arg == 0 || arg > MAX_NR_CONSOLES)
+ return -ENXIO;
+ return vt_waitactive(arg);
+
+ /*
+ * 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)
+ return -EPERM;
+
+ guard(console_lock)();
+ return vt_reldisp(vc, arg);
+ }
+
+ /*
+ * Disallocate memory associated to VT (but leave VT1)
+ */
+ case VT_DISALLOCATE:
+ if (arg > MAX_NR_CONSOLES)
+ return -ENXIO;
+
+ if (arg == 0) {
+ vt_disallocate_all();
+ break;
+ }
+
+ arg = array_index_nospec(arg - 1, MAX_NR_CONSOLES);
+ return vt_disallocate(arg);
+
+ case VT_RESIZE:
+ {
+ struct vt_sizes __user *vtsizes = up;
+ struct vc_data *vc;
+ ushort ll,cc;
+
+ if (!perm)
+ return -EPERM;
+ if (get_user(ll, &vtsizes->v_rows) ||
+ get_user(cc, &vtsizes->v_cols))
+ return -EFAULT;
+
+ guard(console_lock)();
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ vc = vc_cons[i].d;
+
+ if (vc) {
+ /* FIXME: review v tty lock */
+ ret = __vc_resize(vc_cons[i].d, cc, ll, true);
+ if (ret)
+ return ret;
+ }
+ }
+ break;
+ }
+
+ case VT_RESIZEX:
+ if (!perm)
+ return -EPERM;
+
+ return vt_resizex(vc, up);
+
+ case VT_LOCKSWITCH:
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ return -EPERM;
+ vt_dont_switch = true;
+ break;
+ case VT_UNLOCKSWITCH:
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ return -EPERM;
+ vt_dont_switch = false;
+ break;
+ case VT_GETHIFONTMASK:
+ return put_user(vc->vc_hi_font_mask,
+ (unsigned short __user *)arg);
+ case VT_WAITEVENT:
+ return vt_event_wait_ioctl((struct vt_event __user *)arg);
+
+ case VT_GETCONSIZECSRPOS:
+ {
+ struct vt_consizecsrpos concsr;
+
+ console_lock();
+ concsr.con_cols = vc->vc_cols;
+ concsr.con_rows = vc->vc_rows;
+ concsr.csr_col = vc->state.x;
+ concsr.csr_row = vc->state.y;
+ console_unlock();
+ if (copy_to_user(up, &concsr, sizeof(concsr)))
+ return -EFAULT;
+ return 0;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+void reset_vc(struct vc_data *vc)
+{
+ vc->vc_mode = KD_TEXT;
+ vt_reset_unicode(vc->vc_num);
+ 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;
+ 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;
+
+ guard(console_lock)();
+ vc = vc_con->d;
+ if (!vc)
+ return;
+
+ /* FIXME: review tty ref counting */
+ tty = vc->port.tty;
+ /* SAK should also work in all raw modes and reset them properly. */
+ if (tty)
+ __do_SAK(tty);
+ reset_vc(vc);
+}
+
+#ifdef CONFIG_COMPAT
+
+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);
+ 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);
+ 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,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vc_data *vc = tty->driver_data;
+ struct console_font_op op; /* used in multiple places here */
+ void __user *up = compat_ptr(arg);
+ int perm;
+
+ /*
+ * 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;
+
+ switch (cmd) {
+ /*
+ * these need special handlers for incompatible data structures
+ */
+
+ case KDFONTOP:
+ return compat_kdfontop_ioctl(up, perm, &op, vc);
+
+ case PIO_UNIMAP:
+ case GIO_UNIMAP:
+ return compat_unimap_ioctl(cmd, up, perm, vc);
+
+ /*
+ * 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:
+ return vt_ioctl(tty, cmd, arg);
+
+ /*
+ * the rest has a compatible data structure behind arg,
+ * but we have to convert it to a proper 64 bit pointer.
+ */
+ default:
+ return vt_ioctl(tty, cmd, (unsigned long)up);
+ }
+}
+
+
+#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;
+
+ scoped_guard(console_lock) {
+ /* Graphics mode - up to X */
+ if (disable_vt_switch)
+ 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.
+ */
+ 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.
+ */
+ return -EIO;
+ }
+ }
+ if (vt_waitactive(vt + 1)) {
+ pr_debug("Suspend: Can't switch VCs.");
+ return -EINTR;
+ }
+ 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)
+{
+ guard(console_lock)();
+ disable_vt_switch = !do_switch;
+}
+EXPORT_SYMBOL(pm_set_vt_switch);