Index: linux-2.6.18-xen-3.2.0/drivers/xen/console/console.c =================================================================== --- linux-2.6.18-xen-3.2.0/drivers/xen/console/console.c (revision 2) +++ linux-2.6.18-xen-3.2.0/drivers/xen/console/console.c (working copy) @@ -50,6 +50,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -344,18 +348,388 @@ static struct tty_struct *xencons_tty; static int xencons_priv_irq; static char x_char; +static unsigned char xencons_mode; +static void match(void *unused); +DECLARE_WORK(match_work, match, NULL); +static DEFINE_SPINLOCK(translate_lock); +static char raw_in[1024], pending; +static unsigned current_in; + +typedef struct { + char *in; + char *out; + unsigned char in_len; + unsigned char out_len; +} reverse_entry_t; + +#define ESC_CODE_IN 0x1b + +static reverse_entry_t reverse_escaped[] = { + { "\x1b\x5b\x44", "\xe0\x4b\xe0\xcb", 0, 0 }, // arrow left + { "\x1b\x5b\x41", "\xe0\x48\xe0\xc8", 0, 0 }, // arrow up + { "\x1b\x5b\x43", "\xe0\x4d\xe0\xcd", 0, 0 }, // arrow right + { "\x1b\x5b\x42", "\xe0\x50\xe0\xd0", 0, 0 }, // arrow down + { "\x1b\x5b\x5b\x41", "\x3b\xbb", 0, 0 }, // f1 + { "\x1b\x5b\x5b\x42", "\x3c\xbc", 0, 0 }, // f2 + { "\x1b\x5b\x5b\x43", "\x3d\xbd", 0, 0 }, // f3 + { "\x1b\x5b\x5b\x44", "\x3e\xbe", 0, 0 }, // f4 + { "\x1b\x5b\x5b\x45", "\x3f\xbf", 0, 0 }, // f5 + { "\x1b\x5b\x31\x37\x7e", "\x40\xc0", 0, 0 }, // f6 + { "\x1b\x5b\x31\x38\x7e", "\x41\xc1", 0, 0 }, // f7 + { "\x1b\x5b\x31\x39\x7e", "\x42\xc2", 0, 0 }, // f8 + { "\x1b\x5b\x32\x30\x7e", "\x43\xc3", 0, 0 }, // f9 + { "\x1b\x5b\x32\x31\x7e", "\x44\xc4", 0, 0 }, // f10 + { "\x1b\x5b\x32\x33\x7e", "\x57\xd7", 0, 0 }, // f11 + { "\x1b\x5b\x32\x34\x7e", "\x58\xd8", 0, 0 }, // f12 + { "\x1b\x5b\x50", "\x77\xf7", 0, 0 }, // pause + { "\x1b\x5b\x32\x7e", "\x6e\xee", 0, 0 }, // ins + { "\x1b\x5b\x31\x7e", "\x66\xe6", 0, 0 }, // home + { "\x1b\x5b\x35\x7e", "\x68\xe8", 0, 0 }, // pg up + { "\x1b\x5b\x33\x7e", "\x6f\xef", 0, 0 }, // del + { "\x1b\x5b\x34\x7e", "\x6b\xeb", 0, 0 }, // end + { "\x1b\x5b\x36\x7e", "\x6d\xed", 0, 0 }, // pg down + { "\x1b\x5b\x31\x7e", "\x47\xc7", 0, 0 }, // num - home + { "\x1b\x5b\x41", "\x48\xc8", 0, 0 }, // num - up + { "\x1b\x5b\x35\x7e", "\x49\xc9", 0, 0 }, // num - pg up + { "\x1b\x5b\x44", "\x4b\xcb", 0, 0 }, // num - arrow left + { "\x1b\x5b\x47", "\x4c\xcc", 0, 0 }, // num - center + { "\x1b\x5b\x43", "\x4d\xcd", 0, 0 }, // num - arrow right + { "\x1b\x5b\x34\x7e", "\x4f\xcf", 0, 0 }, // num - end + { "\x1b\x5b\x42", "\x50\xd0", 0, 0 }, // num - arrow down + { "\x1b\x5b\x36\x7e", "\x51\xd1", 0, 0 }, // num - pg down + { "\x1b\x5b\x32\x7e", "\x52\xd2", 0, 0 }, // num - ins + { "\x1b\x5b\x33\x7e", "\x53\xd3", 0, 0 }, // num - del + /* ctrl + f[1-12] */ + { "\033[35~", "\x1d\x3b\xbb\x9d", 0, 0 }, + { "\033[36~", "\x1d\x3c\xbc\x9d", 0, 0 }, + { "\033[37~", "\x1d\x3d\xbd\x9d", 0, 0 }, + { "\033[38~", "\x1d\x3e\xbe\x9d", 0, 0 }, + { "\033[39~", "\x1d\x3f\xbf\x9d", 0, 0 }, + { "\033[40~", "\x1d\x40\xc0\x9d", 0, 0 }, + { "\033[41~", "\x1d\x41\xc1\x9d", 0, 0 }, + { "\033[42~", "\x1d\x42\xc2\x9d", 0, 0 }, + { "\033[43~", "\x1d\x43\xc3\x9d", 0, 0 }, + { "\033[44~", "\x1d\x44\xc4\x9d", 0, 0 }, + { "\033[45~", "\x1d\x57\xd7\x9d", 0, 0 }, + { "\033[46~", "\x1d\x58\xd8\x9d", 0, 0 }, + /* alt + f[1-12] */ + { "\033[55~", "\x38\x3b\xbb\xb8", 0, 0 }, + { "\033[56~", "\x38\x3c\xbc\xb8", 0, 0 }, + { "\033[57~", "\x38\x3d\xbd\xb8", 0, 0 }, + { "\033[58~", "\x38\x3e\xbe\xb8", 0, 0 }, + { "\033[59~", "\x38\x3f\xbf\xb8", 0, 0 }, + { "\033[60~", "\x38\x40\xc0\xb8", 0, 0 }, + { "\033[61~", "\x38\x41\xc1\xb8", 0, 0 }, + { "\033[62~", "\x38\x42\xc2\xb8", 0, 0 }, + { "\033[63~", "\x38\x43\xc3\xb8", 0, 0 }, + { "\033[64~", "\x38\x44\xc4\xb8", 0, 0 }, + { "\033[65~", "\x38\x57\xd7\xb8", 0, 0 }, + { "\033[66~", "\x38\x58\xd8\xb8", 0, 0 }, + /* shift + F[1-12] */ + { "\033[75~", "\x2a\x3b\xbb\xaa", 0, 0 }, + { "\033[76~", "\x2a\x3c\xbc\xaa", 0, 0 }, + { "\033[77~", "\x2a\x3d\xbd\xaa", 0, 0 }, + { "\033[78~", "\x2a\x3e\xbe\xaa", 0, 0 }, + { "\033[79~", "\x2a\x3f\xbf\xaa", 0, 0 }, + { "\033[80~", "\x2a\x40\xc0\xaa", 0, 0 }, + { "\033[81~", "\x2a\x41\xc1\xaa", 0, 0 }, + { "\033[82~", "\x2a\x42\xc2\xaa", 0, 0 }, + { "\033[83~", "\x2a\x43\xc3\xaa", 0, 0 }, + { "\033[84~", "\x2a\x44\xc4\xaa", 0, 0 }, + { "\033[85~", "\x2a\x57\xd7\xaa", 0, 0 }, + { "\033[86~", "\x2a\x58\xd8\xaa", 0, 0 }, + /* alt + [:alpha:] */ + { "\033[altq~", "\x38\x10\x90\xb8", 0, 0 }, + { "\033[altw~", "\x38\x11\x91\xb8", 0, 0 }, + { "\033[alte~", "\x38\x12\x92\xb8", 0, 0 }, + { "\033[altr~", "\x38\x13\x93\xb8", 0, 0 }, + { "\033[altt~", "\x38\x14\x94\xb8", 0, 0 }, + { "\033[alty~", "\x38\x15\x95\xb8", 0, 0 }, + { "\033[altu~", "\x38\x16\x96\xb8", 0, 0 }, + { "\033[alti~", "\x38\x17\x97\xb8", 0, 0 }, + { "\033[alto~", "\x38\x18\x98\xb8", 0, 0 }, + { "\033[altp~", "\x38\x19\x99\xb8", 0, 0 }, + { "\033[alta~", "\x38\x1e\x9e\xb8", 0, 0 }, + { "\033[alts~", "\x38\x1f\x9f\xb8", 0, 0 }, + { "\033[altd~", "\x38\x20\xa0\xb8", 0, 0 }, + { "\033[altf~", "\x38\x21\xa1\xb8", 0, 0 }, + { "\033[altg~", "\x38\x22\xa2\xb8", 0, 0 }, + { "\033[alth~", "\x38\x23\xa3\xb8", 0, 0 }, + { "\033[altj~", "\x38\x24\xa4\xb8", 0, 0 }, + { "\033[altk~", "\x38\x25\xa5\xb8", 0, 0 }, + { "\033[altl~", "\x38\x26\xa6\xb8", 0, 0 }, + { "\033[altz~", "\x38\x2c\xac\xb8", 0, 0 }, + { "\033[altx~", "\x38\x2d\xad\xb8", 0, 0 }, + { "\033[altc~", "\x38\x2e\xae\xb8", 0, 0 }, + { "\033[altv~", "\x38\x2f\xaf\xb8", 0, 0 }, + { "\033[altb~", "\x38\x30\xb0\xb8", 0, 0 }, + { "\033[altn~", "\x38\x31\xb1\xb8", 0, 0 }, + { "\033[altm~", "\x38\x32\xb2\xb8", 0, 0 }, + /* alt + [:num:] */ + { "\033[altone~", "\x38\x02\x82\xb8", 0, 0 }, + { "\033[alttwo~", "\x38\x03\x83\xb8", 0, 0 }, + { "\033[altthree~", "\x38\x04\x84\xb8", 0, 0 }, + { "\033[altfour~", "\x38\x05\x85\xb8", 0, 0 }, + { "\033[altfive~", "\x38\x06\x86\xb8", 0, 0 }, + { "\033[altsix~", "\x38\x07\x87\xb8", 0, 0 }, + { "\033[altseven~", "\x38\x08\x88\xb8", 0, 0 }, + { "\033[alteight~", "\x38\x09\x89\xb8", 0, 0 }, + { "\033[altnine~", "\x38\x0a\x8a\xb8", 0, 0 }, + { "\033[altzero~", "\x38\x0b\x8b\xb8", 0, 0 }, + + { 0, 0, 0, 0 } +}; + +static reverse_entry_t reverse_short[] = { + { "\x1b", "\x01\x81", 0, 0 }, // esc + { "\x60", "\x29\xa9", 0, 0 }, // ` + { "\x31", "\x02\x82", 0, 0 }, // 1 + { "\x32", "\x03\x83", 0, 0 }, // 2 + { "\x33", "\x04\x84", 0, 0 }, // 3 + { "\x34", "\x05\x85", 0, 0 }, // 4 + { "\x35", "\x06\x86", 0, 0 }, // 5 + { "\x36", "\x07\x87", 0, 0 }, // 6 + { "\x37", "\x08\x88", 0, 0 }, // 7 + { "\x38", "\x09\x89", 0, 0 }, // 8 + { "\x39", "\x0a\x8a", 0, 0 }, // 9 + { "\x30", "\x0b\x8b", 0, 0 }, // 0 + { "\x2d", "\x0c\x8c", 0, 0 }, // - + { "\x3d", "\x0d\x8d", 0, 0 }, // = + { "\x7f", "\x0e\x8e", 0, 0 }, // backspace + { "\x09", "\x0f\x8f", 0, 0 }, // tab + /* Well, that's true only for dionis inteface. + * In the real world [:alpha:] keys below + * must produce 0x2a 0xaa with Shift pressed */ + { "\x71", "\x2a\x10\x90\xaa", 0, 0 }, // q + { "\x77", "\x2a\x11\x91\xaa", 0, 0 }, // w + { "\x65", "\x2a\x12\x92\xaa", 0, 0 }, // e + { "\x72", "\x2a\x13\x93\xaa", 0, 0 }, // r + { "\x74", "\x2a\x14\x94\xaa", 0, 0 }, // t + { "\x79", "\x2a\x15\x95\xaa", 0, 0 }, // y + { "\x75", "\x2a\x16\x96\xaa", 0, 0 }, // u + { "\x69", "\x2a\x17\x97\xaa", 0, 0 }, // i + { "\x6f", "\x2a\x18\x98\xaa", 0, 0 }, // o + { "\x70", "\x2a\x19\x99\xaa", 0, 0 }, // p + { "\x5b", "\x1a\x9a", 0, 0 }, // [ + { "\x5d", "\x1b\x9b", 0, 0 }, // ] + { "\x5c", "\x2b\xab", 0, 0 }, /* \ */ + { "\x61", "\x2a\x1e\x9e\xaa", 0, 0 }, // a + { "\x73", "\x2a\x1f\x9f\xaa", 0, 0 }, // s + { "\x64", "\x2a\x20\xa0\xaa", 0, 0 }, // d + { "\x66", "\x2a\x21\xa1\xaa", 0, 0 }, // f + { "\x67", "\x2a\x22\xa2\xaa", 0, 0 }, // g + { "\x68", "\x2a\x23\xa3\xaa", 0, 0 }, // h + { "\x6a", "\x2a\x24\xa4\xaa", 0, 0 }, // j + { "\x6b", "\x2a\x25\xa5\xaa", 0, 0 }, // k + { "\x6c", "\x2a\x26\xa6\xaa", 0, 0 }, // l + { "\x3b", "\x27\xa7", 0, 0 }, // ; + { "\x27", "\x28\xa8", 0, 0 }, // ' + { "\x0d", "\x1c\x9c", 0, 0 }, // enter + { "\x7a", "\x2a\x2c\xac\xaa", 0, 0 }, // z + { "\x78", "\x2a\x2d\xad\xaa", 0, 0 }, // x + { "\x63", "\x2a\x2e\xae\xaa", 0, 0 }, // c + { "\x76", "\x2a\x2f\xaf\xaa", 0, 0 }, // v + { "\x62", "\x2a\x30\xb0\xaa", 0, 0 }, // b + { "\x6e", "\x2a\x31\xb1\xaa", 0, 0 }, // n + { "\x6d", "\x2a\x32\xb2\xaa", 0, 0 }, // m + { "\x2c", "\x33\xb3", 0, 0 }, // , + { "\x2e", "\x34\xb4", 0, 0 }, // . + { "\x2f", "\x35\xb5", 0, 0 }, // / + { "\x20", "\x39\xb9", 0, 0 }, // space + { "\x1c", "\x63\xe3", 0, 0 }, // prt scr + { "\x2a", "\x37\xb7", 0, 0 }, // num * + { "\x2b", "\x4e\xce", 0, 0 }, // num + + /* with 'shift' pressed: */ + { "\x7e", "\x2a\x29\xa9\xaa", 0, 0 }, // ~ + { "\x21", "\x2a\x02\x82\xaa", 0, 0 }, // ! + { "\x40", "\x2a\x03\x83\xaa", 0, 0 }, // @ + { "\x23", "\x2a\x04\x84\xaa", 0, 0 }, // # + { "\x24", "\x2a\x05\x85\xaa", 0, 0 }, // $ + { "\x25", "\x2a\x06\x86\xaa", 0, 0 }, // % + { "\x5e", "\x2a\x07\x87\xaa", 0, 0 }, // ^ + { "\x26", "\x2a\x08\x88\xaa", 0, 0 }, // & + { "\x28", "\x2a\x0a\x8a\xaa", 0, 0 }, // ( + { "\x29", "\x2a\x0b\x8b\xaa", 0, 0 }, // ) + { "\x5f", "\x2a\x0c\x8c\xaa", 0, 0 }, // _ + /* Again, use inversed behavior for [:alpha:] with Shift: */ + { "\x51", "\x10\x90", 0, 0 }, // Q + { "\x57", "\x11\x91", 0, 0 }, // W + { "\x45", "\x12\x92", 0, 0 }, // E + { "\x52", "\x13\x93", 0, 0 }, // R + { "\x54", "\x14\x94", 0, 0 }, // T + { "\x59", "\x15\x95", 0, 0 }, // Y + { "\x55", "\x16\x96", 0, 0 }, // U + { "\x49", "\x17\x97", 0, 0 }, // I + { "\x4f", "\x18\x98", 0, 0 }, // O + { "\x50", "\x19\x99", 0, 0 }, // P + { "\x7b", "\x2a\x1a\x9a\xaa", 0, 0 }, // { + { "\x7d", "\x2a\x1b\x9b\xaa", 0, 0 }, // } + { "\x7c", "\x2a\x2b\xab\xaa", 0, 0 }, // | + { "\x41", "\x1e\x9e", 0, 0 }, // A + { "\x53", "\x1f\x9f", 0, 0 }, // S + { "\x44", "\x20\xa0", 0, 0 }, // D + { "\x46", "\x21\xa1", 0, 0 }, // F + { "\x47", "\x22\xa2", 0, 0 }, // G + { "\x48", "\x23\xa3", 0, 0 }, // H + { "\x4a", "\x24\xa4", 0, 0 }, // J + { "\x4b", "\x25\xa5", 0, 0 }, // K + { "\x4c", "\x26\xa6", 0, 0 }, // L + { "\x3a", "\x2a\x27\xa7\xaa", 0, 0 }, // : + { "\x22", "\x2a\x28\xa8\xaa", 0, 0 }, // " + { "\x5a", "\x2c\xac", 0, 0 }, // Z + { "\x58", "\x2d\xad", 0, 0 }, // X + { "\x43", "\x2e\xae", 0, 0 }, // C + { "\x56", "\x2f\xaf", 0, 0 }, // V + { "\x42", "\x30\xb0", 0, 0 }, // B + { "\x4e", "\x31\xb1", 0, 0 }, // N + { "\x4d", "\x32\xb2", 0, 0 }, // M + { "\x3c", "\x2a\x33\xb3\xaa", 0, 0 }, // < + { "\x3e", "\x2a\x34\xb4\xaa", 0, 0 }, // > + { "\x3f", "\x2a\x35\xb5\xaa", 0, 0 }, // ? + { 0, 0, 0, 0 } +}; + +static void xencons_tty_in(char *buf, unsigned len) +{ + unsigned i; + + for (i = 0; i < len; ++i) + tty_insert_flip_char(xencons_tty, buf[i], 0); + tty_flip_buffer_push(xencons_tty); +} + +/** + * return -1 if match is not possible + * return 0 if in_len bytes are equal + * return 1 if match is possible with more raw data provided + */ +static int trans_cmp(char *raw, unsigned raw_len, char *in, unsigned in_len) +{ + unsigned i, limit; + int success_means; + + if (raw_len < in_len) { + limit = raw_len; + success_means = 1; + } else { + limit = in_len; + success_means = 0; + } + + for (i = 0; i < limit; ++i) + if (raw[i] != in[i]) + return -1; + + return success_means; +} + +static void match(void *unused) +{ + reverse_entry_t *re, *found; + int cmp_res; + + spin_lock(&translate_lock); + +try_match: + if (!current_in) + goto out; + + found = NULL; + pending = 0; + + re = reverse_escaped; + while (re->in) { + cmp_res = trans_cmp(raw_in, current_in, re->in, re->in_len); + if (0 == cmp_res) { + found = re; + goto fnd; + } else if (1 == cmp_res) { + pending = 1; + } + ++re; + } + + re = reverse_short; + while (re->in) { + cmp_res = trans_cmp(raw_in, current_in, re->in, re->in_len); + if (0 == cmp_res) { + found = re; + goto fnd; + } + ++re; + } +fnd: + if (found) { + xencons_tty_in(found->out, found->out_len); + current_in -= found->in_len; + if (current_in) { + memmove(raw_in, raw_in + found->in_len, current_in); + goto try_match; + } + } else if (!pending) { + /* there is no match possible with more data provided, + * so remove heading garbage and try again + */ + --current_in; + if (current_in) { + memmove(raw_in, raw_in + 1, current_in); + goto try_match; + } + } +out: + spin_unlock(&translate_lock); +} + +/** + * Idea is to wait for more data if ESC code was found. + * It prevents from matching start of escaped sequence to bare esc code. + */ +static void translate(char *in, unsigned in_len) +{ + unsigned i; + + /* TODO: what? clear raw_in? */ + if (in_len > sizeof(raw_in) - current_in) + return; + + spin_lock(&translate_lock); + memcpy(raw_in + current_in, in, in_len); + current_in += in_len; + spin_unlock(&translate_lock); + + for (i = 0; i < in_len; ++i) + if (ESC_CODE_IN == in[i]) { + schedule_delayed_work(&match_work, HZ/10); + return; + } + + if (!match_work.pending) + match(NULL); +} + +static void reset_trans(void) +{ + spin_lock(&translate_lock); + cancel_delayed_work(&match_work); + pending = 0; + current_in = 0; + spin_unlock(&translate_lock); +} + void xencons_rx(char *buf, unsigned len, struct pt_regs *regs) { - int i; +#ifdef CONFIG_MAGIC_SYSRQ + unsigned i; +#endif unsigned long flags; spin_lock_irqsave(&xencons_lock, flags); if (xencons_tty == NULL) goto out; +#ifdef CONFIG_MAGIC_SYSRQ for (i = 0; i < len; i++) { -#ifdef CONFIG_MAGIC_SYSRQ if (sysrq_enabled) { if (buf[i] == '\x0f') { /* ^O */ if (!sysrq_requested) { @@ -378,11 +752,12 @@ } } } + } #endif - tty_insert_flip_char(xencons_tty, buf[i], 0); - } - tty_flip_buffer_push(xencons_tty); - + if (K_RAW == xencons_mode) + translate(buf, len); + else + xencons_tty_in(buf, len); out: spin_unlock_irqrestore(&xencons_lock, flags); } @@ -593,8 +968,10 @@ spin_lock_irqsave(&xencons_lock, flags); tty->driver_data = NULL; - if (xencons_tty == NULL) + if (xencons_tty == NULL) { xencons_tty = tty; + xencons_mode = K_XLATE; + } __xencons_tx_flush(); spin_unlock_irqrestore(&xencons_lock, flags); @@ -628,9 +1005,46 @@ tty->closing = 0; spin_lock_irqsave(&xencons_lock, flags); xencons_tty = NULL; + xencons_mode = K_XLATE; spin_unlock_irqrestore(&xencons_lock, flags); + reset_trans(); } +static int xencons_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + unsigned char ucval, perm; + long val; + + perm = 0; + if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) + perm = 1; + + switch (cmd) { + case KDGKBTYPE: + ucval = KB_101; + return put_user(ucval, (char __user *)arg); + + case KDGKBMODE: + val = xencons_mode; + return put_user(val, (long __user *)arg); + + case KDSKBMODE: + if (!perm) + return -EPERM; + if (K_RAW != arg + && K_XLATE != arg + && K_MEDIUMRAW != arg + && K_UNICODE != arg) + return -EINVAL; + xencons_mode = arg; + reset_trans(); + tty_ldisc_flush(tty); + return 0; + } + return -ENOIOCTLCMD; +} + static struct tty_operations xencons_ops = { .open = xencons_open, .close = xencons_close, @@ -644,12 +1058,29 @@ .throttle = xencons_throttle, .unthrottle = xencons_unthrottle, .wait_until_sent = xencons_wait_until_sent, + .ioctl = xencons_ioctl, }; static int __init xencons_init(void) { int rc; + reverse_entry_t *re; + /* initialize reverse tables */ + re = reverse_escaped; + while (re->in) { + re->in_len = strlen(re->in); + re->out_len = strlen(re->out); + ++re; + } + + re = reverse_short; + while (re->in) { + re->in_len = strlen(re->in); + re->out_len = strlen(re->out); + ++re; + } + if (!is_running_on_xen()) return -ENODEV;