There were several keys not mapped in the keyboard scancode to "keycode" mapping functions. This series adds in a number of additional keyboard mappings. I used the Phoenix "System BIOS for IBM PC/XT/AT Computers and Compatibles" book as a reference for the mappings.
Albert - can you test if this series fixes your issue with the SysReq key?
I've also uploaded the series to: https://github.com/KevinOConnor/seabios/tree/testing
FYI, I've found on my Linux/X-Windows setup that QEMU doesn't always pass the correct keycodes for SysReq and Break to the guest. But, I've tested on real hardware to confirm that the keyboard mappings are correct.
-Kevin
Kevin O'Connor (10): kbd: Implement 101-key keyboard keycode mapping kbd: Implement extended keycode mappings for keypad-enter and keypad-/ kbd: Suppress keys without mappings kbd: Merge bda->kbd_flag0 and bda->kbd_flag1 kbd: Extract out shift flag setting into new function kbd: Move checking for special keys in __process_keys() into switch kbd: Ignore fake shift keys usb-hid: Generate Ctrl+Break and Alt+SysReq keys kbd: Generate interrupt events for SysReq, PrtScr, and Break post: Map int 0x05 to entry point
src/hw/usb-hid.c | 50 +++++--- src/kbd.c | 383 ++++++++++++++++++++++++++++++------------------------- src/post.c | 1 + src/romlayout.S | 2 + src/std/bda.h | 26 +++- 5 files changed, 268 insertions(+), 194 deletions(-)
The current keyboard code only supports the keycode mappings for 83-key keyboards (plus handling for alt-esc and f11/f12). Add a more complete mapping of keycodes for 101-key keyboards. This adds more alt key and ctrl key mappings.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 68 +++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 28 deletions(-)
diff --git a/src/kbd.c b/src/kbd.c index 61d9df0..2e43e71 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -94,7 +94,16 @@ dequeue_key(struct bregs *regs, int incr, int extended)
u16 keycode = GET_FARVAR(SEG_BDA, *(u16*)(buffer_head+0)); u8 ascii = keycode & 0xff; - if ((ascii == 0xf0 && keycode > 0xff) || (ascii == 0xe0 && !extended)) + if (!extended) { + // Translate extended keys + if (ascii == 0xe0) + keycode &=0xff00; + // Technically, if the ascii value is 0xf0 or if the + // 'scancode' is greater than 0x84 then the key should be + // discarded. However, there seems no harm in passing on the + // extended values in these cases. + } + if (ascii == 0xf0) keycode &= 0xff00; regs->ax = keycode;
@@ -286,7 +295,7 @@ static struct scaninfo { u16 alt; } scan_to_keycode[] VAR16 = { { none, none, none, none }, - { 0x011b, 0x011b, 0x011b, 0x0100 }, /* escape */ + { 0x011b, 0x011b, 0x011b, 0x01f0 }, /* escape */ { 0x0231, 0x0221, none, 0x7800 }, /* 1! */ { 0x0332, 0x0340, 0x0300, 0x7900 }, /* 2@ */ { 0x0433, 0x0423, none, 0x7a00 }, /* 3# */ @@ -299,8 +308,8 @@ static struct scaninfo { { 0x0b30, 0x0b29, none, 0x8100 }, /* 0) */ { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* -_ */ { 0x0d3d, 0x0d2b, none, 0x8300 }, /* =+ */ - { 0x0e08, 0x0e08, 0x0e7f, none }, /* backspace */ - { 0x0f09, 0x0f00, none, none }, /* tab */ + { 0x0e08, 0x0e08, 0x0e7f, 0x0ef0 }, /* backspace */ + { 0x0f09, 0x0f00, 0x9400, 0xa5f0 }, /* tab */ { 0x1071, 0x1051, 0x1011, 0x1000 }, /* Q */ { 0x1177, 0x1157, 0x1117, 0x1100 }, /* W */ { 0x1265, 0x1245, 0x1205, 0x1200 }, /* E */ @@ -311,9 +320,9 @@ static struct scaninfo { { 0x1769, 0x1749, 0x1709, 0x1700 }, /* I */ { 0x186f, 0x184f, 0x180f, 0x1800 }, /* O */ { 0x1970, 0x1950, 0x1910, 0x1900 }, /* P */ - { 0x1a5b, 0x1a7b, 0x1a1b, none }, /* [{ */ - { 0x1b5d, 0x1b7d, 0x1b1d, none }, /* ]} */ - { 0x1c0d, 0x1c0d, 0x1c0a, none }, /* Enter */ + { 0x1a5b, 0x1a7b, 0x1a1b, 0x1af0 }, /* [{ */ + { 0x1b5d, 0x1b7d, 0x1b1d, 0x1bf0 }, /* ]} */ + { 0x1c0d, 0x1c0d, 0x1c0a, 0x1cf0 }, /* Enter */ { none, none, none, none }, /* L Ctrl */ { 0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* A */ { 0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* S */ @@ -324,11 +333,11 @@ static struct scaninfo { { 0x246a, 0x244a, 0x240a, 0x2400 }, /* J */ { 0x256b, 0x254b, 0x250b, 0x2500 }, /* K */ { 0x266c, 0x264c, 0x260c, 0x2600 }, /* L */ - { 0x273b, 0x273a, none, none }, /* ;: */ - { 0x2827, 0x2822, none, none }, /* '" */ - { 0x2960, 0x297e, none, none }, /* `~ */ + { 0x273b, 0x273a, none, 0x27f0 }, /* ;: */ + { 0x2827, 0x2822, none, 0x28f0 }, /* '" */ + { 0x2960, 0x297e, none, 0x29f0 }, /* `~ */ { none, none, none, none }, /* L shift */ - { 0x2b5c, 0x2b7c, 0x2b1c, none }, /* |\ */ + { 0x2b5c, 0x2b7c, 0x2b1c, 0x2bf0 }, /* |\ */ { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* Z */ { 0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* X */ { 0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* C */ @@ -336,11 +345,11 @@ static struct scaninfo { { 0x3062, 0x3042, 0x3002, 0x3000 }, /* B */ { 0x316e, 0x314e, 0x310e, 0x3100 }, /* N */ { 0x326d, 0x324d, 0x320d, 0x3200 }, /* M */ - { 0x332c, 0x333c, none, none }, /* ,< */ - { 0x342e, 0x343e, none, none }, /* .> */ - { 0x352f, 0x353f, none, none }, /* /? */ + { 0x332c, 0x333c, none, 0x33f0 }, /* ,< */ + { 0x342e, 0x343e, none, 0x34f0 }, /* .> */ + { 0x352f, 0x353f, none, 0x35f0 }, /* /? */ { none, none, none, none }, /* R Shift */ - { 0x372a, 0x372a, none, none }, /* * */ + { 0x372a, 0x372a, 0x9600, 0x37f0 }, /* * */ { none, none, none, none }, /* L Alt */ { 0x3920, 0x3920, 0x3920, 0x3920 }, /* space */ { none, none, none, none }, /* caps lock */ @@ -357,19 +366,19 @@ static struct scaninfo { { none, none, none, none }, /* Num Lock */ { none, none, none, none }, /* Scroll Lock */ { 0x4700, 0x4737, 0x7700, none }, /* 7 Home */ - { 0x4800, 0x4838, none, none }, /* 8 UP */ + { 0x4800, 0x4838, 0x8d00, none }, /* 8 UP */ { 0x4900, 0x4939, 0x8400, none }, /* 9 PgUp */ - { 0x4a2d, 0x4a2d, none, none }, /* - */ + { 0x4a2d, 0x4a2d, 0x8e00, 0x4af0 }, /* - */ { 0x4b00, 0x4b34, 0x7300, none }, /* 4 Left */ - { 0x4c00, 0x4c35, none, none }, /* 5 */ + { 0x4c00, 0x4c35, 0x8f00, none }, /* 5 */ { 0x4d00, 0x4d36, 0x7400, none }, /* 6 Right */ - { 0x4e2b, 0x4e2b, none, none }, /* + */ + { 0x4e2b, 0x4e2b, 0x9000, 0x4ef0 }, /* + */ { 0x4f00, 0x4f31, 0x7500, none }, /* 1 End */ - { 0x5000, 0x5032, none, none }, /* 2 Down */ + { 0x5000, 0x5032, 0x9100, none }, /* 2 Down */ { 0x5100, 0x5133, 0x7600, none }, /* 3 PgDn */ - { 0x5200, 0x5230, none, none }, /* 0 Ins */ - { 0x5300, 0x532e, none, none }, /* Del */ - { none, none, none, none }, + { 0x5200, 0x5230, 0x9200, none }, /* 0 Ins */ + { 0x5300, 0x532e, 0x9300, none }, /* Del */ + { none, none, none, none }, /* SysReq */ { none, none, none, none }, { 0x565c, 0x567c, none, none }, /* | */ { 0x8500, 0x8700, 0x8900, 0x8b00 }, /* F11 */ @@ -401,7 +410,7 @@ __process_key(u8 scancode) return; }
- // XXX - PrtScr should cause int 0x05 + // XXX - PrtScr should cause int 0x05 (ctrl-prtscr has keycode 0x7200?) // XXX - Ctrl+Break should cause int 0x1B // XXX - SysReq should cause int 0x15/0x85
@@ -510,10 +519,6 @@ __process_key(u8 scancode) keycode = GET_GLOBAL(info->alt); } else if (flags0 & KF0_CTRLACTIVE) { keycode = GET_GLOBAL(info->control); - } else if (flags2 & KF2_LAST_E0 - && scancode >= 0x47 && scancode <= 0x53) { - /* extended keys handling */ - keycode = (scancode << 8) | 0xe0; } else { u8 useshift = flags0 & (KF0_RSHIFT|KF0_LSHIFT) ? 1 : 0; u8 ascii = GET_GLOBAL(info->normal) & 0xff; @@ -526,6 +531,13 @@ __process_key(u8 scancode) else keycode = GET_GLOBAL(info->normal); } + if (flags2 & KF2_LAST_E0 && scancode >= 0x47 && scancode <= 0x53) { + /* extended keys handling */ + if (flags0 & KF0_ALTACTIVE) + keycode = (scancode + 0x50) << 8; + else + keycode = (keycode & 0xff00) | 0xe0; + } if (!keycode) dprintf(1, "KBD: keycode is zero?\n"); enqueue_key(keycode);
On a 101-key keyboard the keypad enter and keypad '/' keys have unique BIOS keycodes that are distinct from the main keyboard enter and '/' keys.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/src/kbd.c b/src/kbd.c index 2e43e71..b7b34ee 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -98,6 +98,14 @@ dequeue_key(struct bregs *regs, int incr, int extended) // Translate extended keys if (ascii == 0xe0) keycode &=0xff00; + else if ((keycode & 0xff00) == 0xe000) { + if (ascii == 0x2f) + // Extended '/' key + keycode = 0x352f; + else + // Extended enter key + keycode = 0x1c00 | (keycode & 0x00ff); + } // Technically, if the ascii value is 0xf0 or if the // 'scancode' is greater than 0x84 then the key should be // discarded. However, there seems no harm in passing on the @@ -385,6 +393,13 @@ static struct scaninfo { { 0x8600, 0x8800, 0x8a00, 0x8c00 }, /* F12 */ };
+struct scaninfo key_ext_enter VAR16 = { + 0xe00d, 0xe00d, 0xe00a, 0xa600 +}; +struct scaninfo key_ext_slash VAR16 = { + 0xe02f, 0xe02f, 0x9500, 0xa400 +}; + // Handle a ps2 style scancode read from the keyboard. static void __process_key(u8 scancode) @@ -515,6 +530,8 @@ __process_key(u8 scancode) } u16 keycode; struct scaninfo *info = &scan_to_keycode[scancode]; + if (flags2 & KF2_LAST_E0 && (scancode == 0x1c || scancode == 0x35)) + info = (scancode == 0x1c ? &key_ext_enter : &key_ext_slash); if (flags0 & KF0_ALTACTIVE) { keycode = GET_GLOBAL(info->alt); } else if (flags0 & KF0_CTRLACTIVE) {
Don't warn if a key without a mapping is pressed - it's known that some keys aren't mapped to keycodes. Suppress these keys instead of sending 0x0000 to the keyboard buffer - as 0x0000 can confuse some programs.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/kbd.c b/src/kbd.c index b7b34ee..b889cf7 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -555,9 +555,8 @@ __process_key(u8 scancode) else keycode = (keycode & 0xff00) | 0xe0; } - if (!keycode) - dprintf(1, "KBD: keycode is zero?\n"); - enqueue_key(keycode); + if (keycode) + enqueue_key(keycode); break; } flags2 &= ~KF2_LAST_E0;
Merge the two consecutive u8 fields into a single u16 field.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 87 +++++++++++++++++++++-------------------------------------- src/std/bda.h | 26 +++++++++++++++--- 2 files changed, 54 insertions(+), 59 deletions(-)
diff --git a/src/kbd.c b/src/kbd.c index b889cf7..c45e8ea 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -15,34 +15,12 @@ #include "string.h" // memset #include "util.h" // kbd_init
-// Bit definitions for BDA kbd_flag[012] -#define KF0_RSHIFT (1<<0) -#define KF0_LSHIFT (1<<1) -#define KF0_CTRLACTIVE (1<<2) -#define KF0_ALTACTIVE (1<<3) -#define KF0_SCROLLACTIVE (1<<4) -#define KF0_NUMACTIVE (1<<5) -#define KF0_CAPSACTIVE (1<<6) - -#define KF1_LCTRL (1<<0) -#define KF1_LALT (1<<1) -#define KF1_PAUSEACTIVE (1<<3) -#define KF1_SCROLL (1<<4) -#define KF1_NUM (1<<5) -#define KF1_CAPS (1<<6) - -#define KF2_LAST_E1 (1<<0) -#define KF2_LAST_E0 (1<<1) -#define KF2_RCTRL (1<<2) -#define KF2_RALT (1<<3) -#define KF2_101KBD (1<<4) - void kbd_init(void) { dprintf(3, "init keyboard\n"); u16 x = offsetof(struct bios_data_area_s, kbd_buf); - SET_BDA(kbd_flag2, KF2_101KBD); + SET_BDA(kbd_flag1, KF1_101KBD); SET_BDA(kbd_buf_head, x); SET_BDA(kbd_buf_tail, x); SET_BDA(kbd_buf_start_offset, x); @@ -214,9 +192,8 @@ static void handle_1612(struct bregs *regs) { yield(); - regs->al = GET_BDA(kbd_flag0); - regs->ah = ((GET_BDA(kbd_flag1) & ~(KF2_RCTRL|KF2_RALT)) - | (GET_BDA(kbd_flag2) & (KF2_RCTRL|KF2_RALT))); + regs->ax = ((GET_BDA(kbd_flag0) & ~((KF1_RCTRL|KF1_RALT) << 8)) + | ((GET_BDA(kbd_flag1) & (KF1_RCTRL|KF1_RALT)) << 8)); //BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX); }
@@ -404,18 +381,17 @@ struct scaninfo key_ext_slash VAR16 = { static void __process_key(u8 scancode) { - u8 flags0 = GET_BDA(kbd_flag0); + u16 flags0 = GET_BDA(kbd_flag0); u8 flags1 = GET_BDA(kbd_flag1); - u8 flags2 = GET_BDA(kbd_flag2);
- if (flags2 & KF2_LAST_E1) { + if (flags1 & KF1_LAST_E1) { // Part of "pause" key (sequence is e1 1d 45 e1 9d c5) if ((scancode & ~0x80) == 0x1d) // Second key of sequence return; // Third key of sequence - clear flag. - flags2 &= ~KF2_LAST_E1; - SET_BDA(kbd_flag2, flags2); + flags1 &= ~KF1_LAST_E1; + SET_BDA(kbd_flag1, flags1);
if (scancode == 0xc5) { // Final key in sequence. @@ -436,10 +412,10 @@ __process_key(u8 scancode)
case 0x3a: /* Caps Lock press */ flags0 ^= KF0_CAPSACTIVE; - flags1 |= KF1_CAPS; + flags0 |= KF0_CAPS; break; case 0xba: /* Caps Lock release */ - flags1 &= ~KF1_CAPS; + flags0 &= ~KF0_CAPS; break;
case 0x2a: /* L Shift press */ @@ -458,58 +434,58 @@ __process_key(u8 scancode)
case 0x1d: /* Ctrl press */ flags0 |= KF0_CTRLACTIVE; - if (flags2 & KF2_LAST_E0) - flags2 |= KF2_RCTRL; + if (flags1 & KF1_LAST_E0) + flags1 |= KF1_RCTRL; else - flags1 |= KF1_LCTRL; + flags0 |= KF0_LCTRL; break; case 0x9d: /* Ctrl release */ flags0 &= ~KF0_CTRLACTIVE; - if (flags2 & KF2_LAST_E0) - flags2 &= ~KF2_RCTRL; + if (flags1 & KF1_LAST_E0) + flags1 &= ~KF1_RCTRL; else - flags1 &= ~KF1_LCTRL; + flags0 &= ~KF0_LCTRL; break;
case 0x38: /* Alt press */ flags0 |= KF0_ALTACTIVE; - if (flags2 & KF2_LAST_E0) - flags2 |= KF2_RALT; + if (flags1 & KF1_LAST_E0) + flags1 |= KF1_RALT; else - flags1 |= KF1_LALT; + flags0 |= KF0_LALT; break; case 0xb8: /* Alt release */ flags0 &= ~KF0_ALTACTIVE; - if (flags2 & KF2_LAST_E0) - flags2 &= ~KF2_RALT; + if (flags1 & KF1_LAST_E0) + flags1 &= ~KF1_RALT; else - flags1 &= ~KF1_LALT; + flags0 &= ~KF0_LALT; break;
case 0x45: /* Num Lock press */ - flags1 |= KF1_NUM; + flags0 |= KF0_NUM; flags0 ^= KF0_NUMACTIVE; break; case 0xc5: /* Num Lock release */ - flags1 &= ~KF1_NUM; + flags0 &= ~KF0_NUM; break;
case 0x46: /* Scroll Lock press */ - flags1 |= KF1_SCROLL; + flags0 |= KF0_SCROLL; flags0 ^= KF0_SCROLLACTIVE; break; case 0xc6: /* Scroll Lock release */ - flags1 &= ~KF1_SCROLL; + flags0 &= ~KF0_SCROLL; break;
case 0xe0: // Extended key - flags2 |= KF2_LAST_E0; - SET_BDA(kbd_flag2, flags2); + flags1 |= KF1_LAST_E0; + SET_BDA(kbd_flag1, flags1); return; case 0xe1: // Start of pause key sequence - flags2 |= KF2_LAST_E1; + flags1 |= KF1_LAST_E1; break;
default: @@ -530,7 +506,7 @@ __process_key(u8 scancode) } u16 keycode; struct scaninfo *info = &scan_to_keycode[scancode]; - if (flags2 & KF2_LAST_E0 && (scancode == 0x1c || scancode == 0x35)) + if (flags1 & KF1_LAST_E0 && (scancode == 0x1c || scancode == 0x35)) info = (scancode == 0x1c ? &key_ext_enter : &key_ext_slash); if (flags0 & KF0_ALTACTIVE) { keycode = GET_GLOBAL(info->alt); @@ -548,7 +524,7 @@ __process_key(u8 scancode) else keycode = GET_GLOBAL(info->normal); } - if (flags2 & KF2_LAST_E0 && scancode >= 0x47 && scancode <= 0x53) { + if (flags1 & KF1_LAST_E0 && scancode >= 0x47 && scancode <= 0x53) { /* extended keys handling */ if (flags0 & KF0_ALTACTIVE) keycode = (scancode + 0x50) << 8; @@ -559,11 +535,10 @@ __process_key(u8 scancode) enqueue_key(keycode); break; } - flags2 &= ~KF2_LAST_E0; + flags1 &= ~KF1_LAST_E0;
SET_BDA(kbd_flag0, flags0); SET_BDA(kbd_flag1, flags1); - SET_BDA(kbd_flag2, flags2); }
void diff --git a/src/std/bda.h b/src/std/bda.h index 4ad6605..90d64a6 100644 --- a/src/std/bda.h +++ b/src/std/bda.h @@ -30,8 +30,7 @@ struct bios_data_area_s { u16 mem_size_kb; u8 pad2; u8 ps2_ctrl_flag; - u8 kbd_flag0; - u8 kbd_flag1; + u16 kbd_flag0; u8 alt_keypad; u16 kbd_buf_head; u16 kbd_buf_tail; @@ -85,7 +84,7 @@ struct bios_data_area_s { // 40:90 u8 floppy_media_state[4]; u8 floppy_track[2]; - u8 kbd_flag2; + u8 kbd_flag1; u8 kbd_led; struct segoff_s user_wait_complete_flag; u32 user_wait_timeout; @@ -111,6 +110,27 @@ struct bios_data_area_s { #define FMS_DOUBLE_STEPPING (1<<5) #define FMS_DATA_RATE_MASK (0xc0)
+// BDA kbd_flag[01] bitdefs +#define KF0_RSHIFT (1<<0) +#define KF0_LSHIFT (1<<1) +#define KF0_CTRLACTIVE (1<<2) +#define KF0_ALTACTIVE (1<<3) +#define KF0_SCROLLACTIVE (1<<4) +#define KF0_NUMACTIVE (1<<5) +#define KF0_CAPSACTIVE (1<<6) +#define KF0_LCTRL (1<<8) +#define KF0_LALT (1<<9) +#define KF0_PAUSEACTIVE (1<<11) +#define KF0_SCROLL (1<<12) +#define KF0_NUM (1<<13) +#define KF0_CAPS (1<<14) + +#define KF1_LAST_E1 (1<<0) +#define KF1_LAST_E0 (1<<1) +#define KF1_RCTRL (1<<2) +#define KF1_RALT (1<<3) +#define KF1_101KBD (1<<4) + // Limit of BDA timer_counter field #define TICKS_PER_DAY 1573040
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 105 +++++++++++++++++++++++++------------------------------------- 1 file changed, 43 insertions(+), 62 deletions(-)
diff --git a/src/kbd.c b/src/kbd.c index c45e8ea..7ab440e 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -377,21 +377,35 @@ struct scaninfo key_ext_slash VAR16 = { 0xe02f, 0xe02f, 0x9500, 0xa400 };
-// Handle a ps2 style scancode read from the keyboard. static void -__process_key(u8 scancode) +kbd_set_flag(int key_release, u16 set_bit0, u8 set_bit1, u16 toggle_bit) { u16 flags0 = GET_BDA(kbd_flag0); u8 flags1 = GET_BDA(kbd_flag1); + if (key_release) { + flags0 &= ~set_bit0; + flags1 &= ~set_bit1; + } else { + flags0 ^= toggle_bit; + flags0 |= set_bit0; + flags1 |= set_bit1; + } + SET_BDA(kbd_flag0, flags0); + SET_BDA(kbd_flag1, flags1); +}
+// Handle a ps2 style scancode read from the keyboard. +static void +__process_key(u8 scancode) +{ + u8 flags1 = GET_BDA(kbd_flag1); if (flags1 & KF1_LAST_E1) { // Part of "pause" key (sequence is e1 1d 45 e1 9d c5) if ((scancode & ~0x80) == 0x1d) // Second key of sequence return; // Third key of sequence - clear flag. - flags1 &= ~KF1_LAST_E1; - SET_BDA(kbd_flag1, flags1); + SET_BDA(kbd_flag1, flags1 & ~KF1_LAST_E1);
if (scancode == 0xc5) { // Final key in sequence. @@ -400,98 +414,69 @@ __process_key(u8 scancode) } return; } + if (flags1 & KF1_LAST_E0) + // Clear E0 flag in memory for next key event + SET_BDA(kbd_flag1, flags1 & ~KF1_LAST_E0);
// XXX - PrtScr should cause int 0x05 (ctrl-prtscr has keycode 0x7200?) // XXX - Ctrl+Break should cause int 0x1B // XXX - SysReq should cause int 0x15/0x85
+ int key_release = scancode & 0x80; switch (scancode) { case 0x00: dprintf(1, "KBD: int09 handler: AL=0\n"); return;
case 0x3a: /* Caps Lock press */ - flags0 ^= KF0_CAPSACTIVE; - flags0 |= KF0_CAPS; - break; case 0xba: /* Caps Lock release */ - flags0 &= ~KF0_CAPS; - break; - + kbd_set_flag(key_release, KF0_CAPS, 0, KF0_CAPSACTIVE); + return; case 0x2a: /* L Shift press */ - flags0 |= KF0_LSHIFT; - break; case 0xaa: /* L Shift release */ - flags0 &= ~KF0_LSHIFT; - break; - + kbd_set_flag(key_release, KF0_LSHIFT, 0, 0); + return; case 0x36: /* R Shift press */ - flags0 |= KF0_RSHIFT; - break; case 0xb6: /* R Shift release */ - flags0 &= ~KF0_RSHIFT; - break; - + kbd_set_flag(key_release, KF0_RSHIFT, 0, 0); + return; case 0x1d: /* Ctrl press */ - flags0 |= KF0_CTRLACTIVE; - if (flags1 & KF1_LAST_E0) - flags1 |= KF1_RCTRL; - else - flags0 |= KF0_LCTRL; - break; case 0x9d: /* Ctrl release */ - flags0 &= ~KF0_CTRLACTIVE; if (flags1 & KF1_LAST_E0) - flags1 &= ~KF1_RCTRL; + kbd_set_flag(key_release, KF0_CTRLACTIVE, KF1_RCTRL, 0); else - flags0 &= ~KF0_LCTRL; - break; - + kbd_set_flag(key_release, KF0_CTRLACTIVE | KF0_LCTRL, 0, 0); + return; case 0x38: /* Alt press */ - flags0 |= KF0_ALTACTIVE; - if (flags1 & KF1_LAST_E0) - flags1 |= KF1_RALT; - else - flags0 |= KF0_LALT; - break; case 0xb8: /* Alt release */ - flags0 &= ~KF0_ALTACTIVE; if (flags1 & KF1_LAST_E0) - flags1 &= ~KF1_RALT; + kbd_set_flag(key_release, KF0_ALTACTIVE, KF1_RALT, 0); else - flags0 &= ~KF0_LALT; - break; - + kbd_set_flag(key_release, KF0_ALTACTIVE | KF0_LALT, 0, 0); + return; case 0x45: /* Num Lock press */ - flags0 |= KF0_NUM; - flags0 ^= KF0_NUMACTIVE; - break; case 0xc5: /* Num Lock release */ - flags0 &= ~KF0_NUM; - break; - + kbd_set_flag(key_release, KF0_NUM, 0, KF0_NUMACTIVE); + return; case 0x46: /* Scroll Lock press */ - flags0 |= KF0_SCROLL; - flags0 ^= KF0_SCROLLACTIVE; - break; case 0xc6: /* Scroll Lock release */ - flags0 &= ~KF0_SCROLL; - break; + kbd_set_flag(key_release, KF0_SCROLL, 0, KF0_SCROLLACTIVE); + return;
case 0xe0: // Extended key - flags1 |= KF1_LAST_E0; - SET_BDA(kbd_flag1, flags1); + SET_BDA(kbd_flag1, flags1 | KF1_LAST_E0); return; case 0xe1: // Start of pause key sequence - flags1 |= KF1_LAST_E1; - break; + SET_BDA(kbd_flag1, flags1 | KF1_LAST_E1); + return;
default: - if (scancode & 0x80) + if (key_release) // toss key releases break; + u16 flags0 = GET_BDA(kbd_flag0); if (scancode == 0x53 && ((flags0 & (KF0_CTRLACTIVE|KF0_ALTACTIVE)) == (KF0_CTRLACTIVE|KF0_ALTACTIVE))) { @@ -535,10 +520,6 @@ __process_key(u8 scancode) enqueue_key(keycode); break; } - flags1 &= ~KF1_LAST_E0; - - SET_BDA(kbd_flag0, flags0); - SET_BDA(kbd_flag1, flags1); }
void
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 136 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 71 insertions(+), 65 deletions(-)
diff --git a/src/kbd.c b/src/kbd.c index 7ab440e..a75adc3 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -398,34 +398,30 @@ kbd_set_flag(int key_release, u16 set_bit0, u8 set_bit1, u16 toggle_bit) static void __process_key(u8 scancode) { + // Check for multi-key sequences u8 flags1 = GET_BDA(kbd_flag1); if (flags1 & KF1_LAST_E1) { // Part of "pause" key (sequence is e1 1d 45 e1 9d c5) if ((scancode & ~0x80) == 0x1d) - // Second key of sequence + // Second key of sequence - ignore return; - // Third key of sequence - clear flag. + // Third key of sequence - clear flag for next key SET_BDA(kbd_flag1, flags1 & ~KF1_LAST_E1); - - if (scancode == 0xc5) { - // Final key in sequence. - - // XXX - do actual pause. - } - return; } if (flags1 & KF1_LAST_E0) // Clear E0 flag in memory for next key event SET_BDA(kbd_flag1, flags1 & ~KF1_LAST_E0);
- // XXX - PrtScr should cause int 0x05 (ctrl-prtscr has keycode 0x7200?) - // XXX - Ctrl+Break should cause int 0x1B - // XXX - SysReq should cause int 0x15/0x85 - + // Check for special keys int key_release = scancode & 0x80; switch (scancode) { - case 0x00: - dprintf(1, "KBD: int09 handler: AL=0\n"); + case 0xe0: + // Extended key + SET_BDA(kbd_flag1, flags1 | KF1_LAST_E0); + return; + case 0xe1: + // Start of pause key sequence + SET_BDA(kbd_flag1, flags1 | KF1_LAST_E1); return;
case 0x3a: /* Caps Lock press */ @@ -456,70 +452,80 @@ __process_key(u8 scancode) return; case 0x45: /* Num Lock press */ case 0xc5: /* Num Lock release */ + if (flags1 & KF1_LAST_E1) + // XXX - pause key. + return; kbd_set_flag(key_release, KF0_NUM, 0, KF0_NUMACTIVE); return; case 0x46: /* Scroll Lock press */ case 0xc6: /* Scroll Lock release */ + if (flags1 & KF1_LAST_E0) + // XXX - Ctrl+Break should cause int 0x1B + return; kbd_set_flag(key_release, KF0_SCROLL, 0, KF0_SCROLLACTIVE); return;
- case 0xe0: - // Extended key - SET_BDA(kbd_flag1, flags1 | KF1_LAST_E0); - return; - case 0xe1: - // Start of pause key sequence - SET_BDA(kbd_flag1, flags1 | KF1_LAST_E1); + case 0x37: + case 0xb7: + if (flags1 & KF1_LAST_E0) + // XXX - PrtScr should cause int 0x05 (ctrl-prtscr keycode 0x7200?) + return; + break; + case 0x54: + case 0xd4: + // XXX - SysReq should cause int 0x15/0x85 return; - - default: - if (key_release) - // toss key releases - break; - u16 flags0 = GET_BDA(kbd_flag0); - if (scancode == 0x53 - && ((flags0 & (KF0_CTRLACTIVE|KF0_ALTACTIVE)) - == (KF0_CTRLACTIVE|KF0_ALTACTIVE))) { + case 0x53: + if ((GET_BDA(kbd_flag0) & (KF0_CTRLACTIVE|KF0_ALTACTIVE)) + == (KF0_CTRLACTIVE|KF0_ALTACTIVE)) { // Ctrl+alt+del - reset machine. SET_BDA(soft_reset_flag, 0x1234); reset(); } - if (scancode >= ARRAY_SIZE(scan_to_keycode)) { - dprintf(1, "KBD: int09h_handler(): unknown scancode read: 0x%02x!\n" - , scancode); - return; - } - u16 keycode; - struct scaninfo *info = &scan_to_keycode[scancode]; - if (flags1 & KF1_LAST_E0 && (scancode == 0x1c || scancode == 0x35)) - info = (scancode == 0x1c ? &key_ext_enter : &key_ext_slash); - if (flags0 & KF0_ALTACTIVE) { - keycode = GET_GLOBAL(info->alt); - } else if (flags0 & KF0_CTRLACTIVE) { - keycode = GET_GLOBAL(info->control); - } else { - u8 useshift = flags0 & (KF0_RSHIFT|KF0_LSHIFT) ? 1 : 0; - u8 ascii = GET_GLOBAL(info->normal) & 0xff; - if ((flags0 & KF0_NUMACTIVE && scancode >= 0x47 && scancode <= 0x53) - || (flags0 & KF0_CAPSACTIVE && ascii >= 'a' && ascii <= 'z')) - // Numlock/capslock toggles shift on certain keys - useshift ^= 1; - if (useshift) - keycode = GET_GLOBAL(info->shift); - else - keycode = GET_GLOBAL(info->normal); - } - if (flags1 & KF1_LAST_E0 && scancode >= 0x47 && scancode <= 0x53) { - /* extended keys handling */ - if (flags0 & KF0_ALTACTIVE) - keycode = (scancode + 0x50) << 8; - else - keycode = (keycode & 0xff00) | 0xe0; - } - if (keycode) - enqueue_key(keycode); break; + + default: + break; + } + + // Handle generic keys + if (key_release) + // ignore key releases + return; + if (!scancode || scancode >= ARRAY_SIZE(scan_to_keycode)) { + dprintf(1, "__process_key unknown scancode read: 0x%02x!\n", scancode); + return; + } + struct scaninfo *info = &scan_to_keycode[scancode]; + if (flags1 & KF1_LAST_E0 && (scancode == 0x1c || scancode == 0x35)) + info = (scancode == 0x1c ? &key_ext_enter : &key_ext_slash); + u16 flags0 = GET_BDA(kbd_flag0); + u16 keycode; + if (flags0 & KF0_ALTACTIVE) { + keycode = GET_GLOBAL(info->alt); + } else if (flags0 & KF0_CTRLACTIVE) { + keycode = GET_GLOBAL(info->control); + } else { + u8 useshift = flags0 & (KF0_RSHIFT|KF0_LSHIFT) ? 1 : 0; + u8 ascii = GET_GLOBAL(info->normal) & 0xff; + if ((flags0 & KF0_NUMACTIVE && scancode >= 0x47 && scancode <= 0x53) + || (flags0 & KF0_CAPSACTIVE && ascii >= 'a' && ascii <= 'z')) + // Numlock/capslock toggles shift on certain keys + useshift ^= 1; + if (useshift) + keycode = GET_GLOBAL(info->shift); + else + keycode = GET_GLOBAL(info->normal); + } + if (flags1 & KF1_LAST_E0 && scancode >= 0x47 && scancode <= 0x53) { + /* extended keys handling */ + if (flags0 & KF0_ALTACTIVE) + keycode = (scancode + 0x50) << 8; + else + keycode = (keycode & 0xff00) | 0xe0; } + if (keycode) + enqueue_key(keycode); }
void
AT keyboards can produce "fake" shift keys on some extended events. It's not necessary to process these artificial events as the actual extended keys are detected directly.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/src/kbd.c b/src/kbd.c index a75adc3..ff962cf 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -430,10 +430,16 @@ __process_key(u8 scancode) return; case 0x2a: /* L Shift press */ case 0xaa: /* L Shift release */ + if (flags1 & KF1_LAST_E0) + // Ignore fake shifts + return; kbd_set_flag(key_release, KF0_LSHIFT, 0, 0); return; case 0x36: /* R Shift press */ case 0xb6: /* R Shift release */ + if (flags1 & KF1_LAST_E0) + // Ignore fake shifts + return; kbd_set_flag(key_release, KF0_RSHIFT, 0, 0); return; case 0x1d: /* Ctrl press */
Detect the sequences for generating Ctrl+Break and Alt+SysReq on USB keyboards and produce the appropriate legacy scancodes.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/usb-hid.c | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-)
diff --git a/src/hw/usb-hid.c b/src/hw/usb-hid.c index 09a6cf4..fa4d9a2 100644 --- a/src/hw/usb-hid.c +++ b/src/hw/usb-hid.c @@ -149,7 +149,7 @@ static u16 KeyToScanCode[] VAR16 = { 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034, 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046, - 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d, + 0xe145, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d, 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e, 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047, 0x0048, 0x0049, 0x0052, 0x0053 @@ -172,42 +172,52 @@ struct keyevent {
// Translate data from KeyToScanCode[] to calls to process_key(). static void -prockeys(u16 keys) +prockeys(u16 scancode, u8 key_release, u8 mods) { - if (keys > 0xff) { - u8 key = keys>>8; - if (key == 0xe1) { - // Pause key - process_key(0xe1); - process_key(0x1d | (keys & RELEASEBIT)); - process_key(0x45 | (keys & RELEASEBIT)); + if (scancode > 0xff) { + if (scancode == 0xe145) { + // XXX - a real AT keyboard would immediately send the key release + if (mods & ((1<<0) | (1<<4))) { + // Cntr+Break key + process_key(0xe0); + process_key(0x46 | key_release); + } else { + // Pause key + process_key(0xe1); + process_key(0x1d | key_release); + process_key(0x45 | key_release); + } + return; + } else if (scancode == 0xe037 && mods & ((1<<2) | (1<<6))) { + // Alt+SysReq key + process_key(0x54 | key_release); return; } - process_key(key); + process_key(0xe0); } - process_key(keys); + process_key(scancode | key_release); }
// Handle a USB key press/release event. static void -procscankey(u8 key, u8 flags) +procscankey(u8 key, u8 key_release, u8 mods) { if (key >= ARRAY_SIZE(KeyToScanCode)) return; - u16 keys = GET_GLOBAL(KeyToScanCode[key]); - if (keys) - prockeys(keys | flags); + u16 scancode = GET_GLOBAL(KeyToScanCode[key]); + if (scancode) + prockeys(scancode, key_release, mods); }
// Handle a USB modifier press/release event. static void -procmodkey(u8 mods, u8 flags) +procmodkey(u8 mods, u8 key_release) { int i; for (i=0; mods; i++) if (mods & (1<<i)) { // Modifier key change. - prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags); + prockeys(GET_GLOBAL(ModifierToScanCode[i]), key_release, 0); mods &= ~(1<<i); } } @@ -245,7 +255,7 @@ handle_key(struct keyevent *data) for (j=0;; j++) { if (j>=ARRAY_SIZE(data->keys)) { // Key released. - procscankey(key, RELEASEBIT); + procscankey(key, RELEASEBIT, data->modifiers); if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1]) // Last pressed key released - disable repeat. old.repeatcount = 0xff; @@ -269,7 +279,7 @@ handle_key(struct keyevent *data) if (!key) continue; // New key pressed. - procscankey(key, 0); + procscankey(key, 0, data->modifiers); old.keys[addpos++] = key; old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1; } @@ -279,7 +289,7 @@ handle_key(struct keyevent *data) // Check for key repeat event. if (addpos) { if (!old.repeatcount) - procscankey(old.keys[addpos-1], 0); + procscankey(old.keys[addpos-1], 0, data->modifiers); else if (old.repeatcount != 0xff) old.repeatcount--; }
Generate the appropriate interrupt events for the given keys.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/kbd.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++----- src/romlayout.S | 2 ++ 2 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/src/kbd.c b/src/kbd.c index ff962cf..6c9290f 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -394,6 +394,49 @@ kbd_set_flag(int key_release, u16 set_bit0, u8 set_bit1, u16 toggle_bit) SET_BDA(kbd_flag1, flags1); }
+static void +kbd_ctrl_break(int key_release) +{ + if (!key_release) + return; + // Clear keyboard buffer and place 0x0000 in buffer + u16 buffer_start = GET_BDA(kbd_buf_start_offset); + SET_BDA(kbd_buf_head, buffer_start); + SET_BDA(kbd_buf_tail, buffer_start+2); + SET_FARVAR(SEG_BDA, *(u16*)(buffer_start+0), 0x0000); + // Set break flag + SET_BDA(break_flag, 0x80); + // Generate int 0x1b + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x1b, &br); +} + +static void +kbd_sysreq(int key_release) +{ + // SysReq generates int 0x15/0x85 + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ah = 0x85; + br.al = key_release ? 0x01 : 0x00; + br.flags = F_IF; + call16_int(0x15, &br); +} + +static void +kbd_prtscr(int key_release) +{ + if (key_release) + return; + // PrtScr generates int 0x05 (ctrl-prtscr has keycode 0x7200?) + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x05, &br); +} + // Handle a ps2 style scancode read from the keyboard. static void __process_key(u8 scancode) @@ -465,21 +508,23 @@ __process_key(u8 scancode) return; case 0x46: /* Scroll Lock press */ case 0xc6: /* Scroll Lock release */ - if (flags1 & KF1_LAST_E0) - // XXX - Ctrl+Break should cause int 0x1B + if (flags1 & KF1_LAST_E0) { + kbd_ctrl_break(key_release); return; + } kbd_set_flag(key_release, KF0_SCROLL, 0, KF0_SCROLLACTIVE); return;
case 0x37: case 0xb7: - if (flags1 & KF1_LAST_E0) - // XXX - PrtScr should cause int 0x05 (ctrl-prtscr keycode 0x7200?) + if (flags1 & KF1_LAST_E0) { + kbd_prtscr(key_release); return; + } break; case 0x54: case 0xd4: - // XXX - SysReq should cause int 0x15/0x85 + kbd_sysreq(key_release); return; case 0x53: if ((GET_BDA(kbd_flag0) & (KF0_CTRLACTIVE|KF0_ALTACTIVE)) diff --git a/src/romlayout.S b/src/romlayout.S index 53cc0f5..89b3784 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -171,12 +171,14 @@ __farcall16: .endm
IRQ_TRAMPOLINE 02 + IRQ_TRAMPOLINE 05 IRQ_TRAMPOLINE 10 IRQ_TRAMPOLINE 13 IRQ_TRAMPOLINE 15 IRQ_TRAMPOLINE 16 IRQ_TRAMPOLINE 18 IRQ_TRAMPOLINE 19 + IRQ_TRAMPOLINE 1b IRQ_TRAMPOLINE 1c IRQ_TRAMPOLINE 4a
int 0x05 was not assigned in the interrupt table - fix that.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/post.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/post.c b/src/post.c index 2c4f12b..2a06d44 100644 --- a/src/post.c +++ b/src/post.c @@ -47,6 +47,7 @@ ivt_init(void)
// Initialize software handlers. SET_IVT(0x02, FUNC16(entry_02)); + SET_IVT(0x05, FUNC16(entry_05)); SET_IVT(0x10, FUNC16(entry_10)); SET_IVT(0x11, FUNC16(entry_11)); SET_IVT(0x12, FUNC16(entry_12));
On Mon, Sep 05, 2016 at 02:36:01PM -0400, Kevin O'Connor wrote:
There were several keys not mapped in the keyboard scancode to "keycode" mapping functions. This series adds in a number of additional keyboard mappings. I used the Phoenix "System BIOS for IBM PC/XT/AT Computers and Compatibles" book as a reference for the mappings.
Albert - can you test if this series fixes your issue with the SysReq key?
I've also uploaded the series to: https://github.com/KevinOConnor/seabios/tree/testing
FYI, I've found on my Linux/X-Windows setup that QEMU doesn't always pass the correct keycodes for SysReq and Break to the guest. But, I've tested on real hardware to confirm that the keyboard mappings are correct.
FYI, I committed this series.
-Kevin