📄 keyboard.cxx
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <kerninc/kernel.hxx>#include <kerninc/MsgLog.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/SysTimer.hxx>#include <kerninc/AutoConf.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/Thread.hxx>#include <kerninc/Key.hxx>#include <kerninc/Invocation.hxx>#include <kerninc/Machine.hxx>#include <eros/i486/io.h>#include "IDT.hxx"static bool Probe(AutoConf *ac);static bool Attach(AutoConf *ac);struct Driver ac_kbd = { "kbd", Probe, Attach} ;/* The keyboard interrupt handler is built as a fast-path interrupt * handler for several reasons: * * 1. It is important that servicing of the console keyboard never be * delayed -- nothing should prevent the system from being rebootable, * for example. * * 2. The labor done in the typical case -- storing a character in a * buffer -- is minimal. * * 3. The interrupt line for the keyboard is exclusive and hardwired, * and not reassigned on any current generation PC's. */const uint8_t KbdDataPort = 0x60u;const uint8_t KbdCtrlPort = 0x64u;const uint8_t KbdStatusPort = 0x64u;ThreadPile KbdSleepQ;int kbd_polling = 0;struct KeyCmd { enum { SetLed = 0xedu, } ;};struct KbdStatus { enum { BufFull = 0x1, Ready = 0x2, };};struct KeyMod { enum { /* Note that the first three values correspond to the bitmask for the * keyboard LED's -- this is not an accident! */ ScrlLock = 0x01u, NumLock = 0x02u, AlphaLock = 0x04u, Shift = 0x10u, Ctrl = 0x20u, Alt = 0x40u, Extended = 0x100u, /* key is an "extended" key */ IsAlpha = 0x200u, /* key is modified by alpha lock key */ IsPad = 0x400u, /* key is modified by num lock key */ Meta = 0x800u, /* key can be meta'd */ } ;};static bool AsciiMode = true;#if 0static uint32_t ShiftState = KeyMod::NumLock | KeyMod::ScrlLock;#elsestatic uint32_t ShiftState = 0;#endif#define KBD_DELAY \ { u_char x = in8(0x84); (void) x; } \ { u_char x = in8(0x84); (void) x; } \ { u_char x = in8(0x84); (void) x; } \ { u_char x = in8(0x84); (void) x; }/* kbd_wait -- wait for a character to be available from the keyboard. */static void KbdWait(void){ int i = 100; while (i--) { if ((inb(KbdStatusPort) & KbdStatus::Ready) == 0) break; Machine::SpinWaitUs(10); }#if 0 printf("KbdWait fails\n");#endif}#if 0static voidWriteKbd(uint8_t c){ KbdWait(); old_outb(KbdDataPort, c);}#endifstatic void KbdCmd(uint8_t command){ int retry = 5; do { int i = 100000; KbdWait(); old_outb(KbdDataPort, command); while (i--) { if (inb(KbdStatusPort) & KbdStatus::BufFull) { int val; /* DELAY(10); */ val = inb(KbdDataPort); if (val == 0xfa) return; if (val == 0xfe) break; } } } while (retry--); printf("KbdCmd fails\n");}static boolReadKbd(uint8_t& c){ /* KbdWait(); */ while ( (inb(KbdStatusPort) & KbdStatus::BufFull) == 0 ) return false; c = inb(KbdDataPort); return true;}static voidUpdateKbdLeds(){ KbdCmd(KeyCmd::SetLed); KbdCmd(ShiftState & 0x7u);} /* Theoretically, a reboot can be accomplished from the keyboard, but * I wasn't able to make it work. The following is a bit more crude, * but equally effective: */static voidReboot(){#if 1 Machine::HardReset();#else old_outb(KbdCtrlPort, 0xd0); /* "read output port" command */ while ( (inb(KbdStatusPort) & KbdStatus::BufFull) == 0 ) ; uint8_t c = inb(KbdDataPort); c |= 0x1; /* set sysreset bit */ old_outb(KbdCtrlPort, 0xd1); /* "write output port" command */ KbdWait(); old_outb(KbdDataPort, c); /* "write output port" command */#endif} const uint32_t KbdBufferSz = 256;uint8_t kbdBuffer[KbdBufferSz];uint32_t kbdBufferTop = 0;/* Keyboard interpretation proceeds in two phases. First, the scan * code is converted into a virtual key code, performing any necessary * keyboard escape translation. Then the key code translation table * is consulted to decide what character to return and whether to * update the shift state (if at all). */#define NOP 256#define NOCHAR(name) {{ NOP, NOP, NOP, NOP }, 0 }#define ALPHA(X) {{ X+32, X, X - 64, X - 64 }, KeyMod::Meta|KeyMod::IsAlpha }#define KEY(X, Y) {{ X, Y, X, Y }, KeyMod::Meta }#define PAD(X, Y) {{ X, Y, X, Y }, KeyMod::IsPad }#define F(X) (X + 256)const uint32_t num_scan = 0x59;struct KeyInfo { uint16_t value[4]; /* base, shift, ctrl, shift-ctrl */ uint16_t flags;} key_table[num_scan] = { NOCHAR(None), /* 0x00 */ KEY('\027', '\027'), /* 0x01 */ KEY('1', '!'), /* 0x02 */ { { '2', '@', '\0', '\0'}, 0 }, /* 0x03 -- generate NUL */ KEY('3', '#'), /* 0x04 */ KEY('4', '$'), /* 0x05 */ KEY('5', '%'), /* 0x06 */ KEY('6', '^'), /* 0x07 */ KEY('7', '&'), /* 0x08 */ KEY('8', '*'), /* 0x09 */ KEY('9', '('), /* 0x0a */ KEY('0', ')'), /* 0x0b */ KEY('-', '_'), /* 0x0c */ KEY('=', '+'), /* 0x0d */ KEY(0x08, 0x08), /* 0x0e */ KEY(0x09, 0x08), /* 0x0f -- is back tab right? */ ALPHA('Q'), /* 0x10 */ ALPHA('W'), /* 0x11 */ ALPHA('E'), /* 0x12 */ ALPHA('R'), /* 0x13 */ ALPHA('T'), /* 0x14 */ ALPHA('Y'), /* 0x15 */ ALPHA('U'), /* 0x16 */ ALPHA('I'), /* 0x17 */ ALPHA('O'), /* 0x18 */ ALPHA('P'), /* 0x19 */ KEY('[', '{'), /* 0x1a */ KEY(']', '}'), /* 0x1b */ KEY('\r', '\r'), /* 0x1c -- enter */ { { NOP, NOP, NOP, NOP }, KeyMod::Ctrl }, /* 0x1d -- lctrl */ ALPHA('A'), /* 0x1e */ ALPHA('S'), /* 0x1f */ ALPHA('D'), /* 0x20 */ ALPHA('F'), /* 0x21 */ ALPHA('G'), /* 0x22 */ ALPHA('H'), /* 0x23 */ ALPHA('J'), /* 0x24 */ ALPHA('K'), /* 0x25 */ ALPHA('L'), /* 0x26 */ KEY(';', ':'), /* 0x27 */ KEY('\'', '"'), /* 0x28 */ KEY('`', '~'), /* 0x29 */ { { NOP, NOP, NOP, NOP }, KeyMod::Shift }, /* 0x2a -- lshift */ KEY('\\', '|'), /* 0x2b */ ALPHA('Z'), /* 0x2c */ ALPHA('X'), /* 0x2d */ ALPHA('C'), /* 0x2e */ ALPHA('V'), /* 0x2f */ ALPHA('B'), /* 0x30 */ ALPHA('N'), /* 0x31 */ ALPHA('M'), /* 0x32 */ KEY(',', '<'), /* 0x33 */ KEY('.', '>'), /* 0x34 */ KEY('/', '?'), /* 0x35 */ { { NOP, NOP, NOP, NOP }, KeyMod::Shift }, /* 0x36 -- rshift */ KEY('*', '*'), /* 0x37 */ { { NOP, NOP, NOP, NOP }, KeyMod::Alt }, /* 0x38 -- lalt */ KEY(' ', ' '), /* 0x39 -- space */ { { NOP, NOP, NOP, NOP }, KeyMod::AlphaLock }, /* 0x3a -- alpha lock */ KEY( F(1), NOP ), /* 0x3b -- F1 */ KEY( F(2), NOP ), /* 0x3c -- F2 */ KEY( F(3), NOP ), /* 0x3d -- F3 */ KEY( F(4), NOP ), /* 0x3e -- F4 */ KEY( F(5), NOP ), /* 0x3f -- F5 */ KEY( F(6), NOP ), /* 0x40 -- F6 */ KEY( F(7), NOP ), /* 0x41 -- F7 */ KEY( F(8), NOP ), /* 0x42 -- F8 */ KEY( F(9), NOP ), /* 0x43 -- F9 */ KEY( F(10), NOP ), /* 0x44 -- F10 */ { { NOP, NOP, NOP, NOP }, KeyMod::NumLock }, /* 0x45 -- num lock */ { { NOP, NOP, NOP, NOP }, KeyMod::ScrlLock }, /* 0x46 -- scroll-lock */ /* Keypad character mappings -- these assume that num-lock is NOT set! */ PAD( F(15), '7' ), /* 0x47 -- keypad 7 */ PAD( F(16), '8' ), /* 0x48 -- keypad 8 */ PAD( F(17), '9' ), /* 0x49 -- keypad 9 */ KEY( '-', NOP ), /* 0x4a -- keypad - */ PAD( F(19), '4' ), /* 0x4b -- keypad 4 */ PAD( NOP, '5' ), /* 0x4c -- keypad 5 */ PAD( F(20), '6' ), /* 0x4d -- keypad 6 */ KEY( '+', '+' ), /* 0x4e -- keypad + */ PAD( F(22), '1' ), /* 0x4f -- keypad 1 */ PAD( F(23), '2' ), /* 0x50 -- keypad 2 */ PAD( F(24), '3' ), /* 0x51 -- keypad 3 */ PAD( F(25), '0' ), /* 0x52 -- keypad 0 */ PAD( 0x7f, '.' ), /* 0x53 -- keypad ./DEL */ KEY( NOP, NOP ), /* 0x54 -- unused! */ KEY( NOP, NOP ), /* 0x55 -- unused! */ KEY( NOP, NOP ), /* 0x56 -- unused! */ KEY( F(11), NOP ), /* 0x57 -- F11 */ KEY( F(12), NOP ), /* 0x58 -- F12 */#if 0 /* CHARACTERS BELOW THIS POINT ARE RECODED!!! */ { { NOP, NOP, NOP, NOP }, KeyMod::Ctrl }, /* 0x59 rctrl */ { { NOP, NOP, NOP, NOP }, KeyMod::Alt }, /* 0x5a ralt */e0,1c kpd-entere0,1d rctrl SUPPRESSEDe0,35 kpd-/e0,37 print-screene0,38 ralt SUPPRESSEDe0,47 home e0,48 uparrowe0,49 PgUpe0,4b left-arrow e0,4d right-arrowe0,4f end e0,50 downarrowe0,51 PgDne0,52 insert e0,53 delete e0,5b lwindowe0,5c rwindowe0,5d menue1,1d,68 pause#endif};voidFetchInputFromKeyboard(){ static uint8_t esc_code = 0; assert(kbdBufferTop < KbdBufferSz); uint8_t scanCode = 0; while ( ReadKbd(scanCode) ) { /* printf("<kc = %x>", scanCode); */ if (AsciiMode == false) { if (kbdBufferTop == KbdBufferSz) fatal("Keyboard buffer overflow\n"); else kbdBuffer[kbdBufferTop++] = scanCode; } else { bool shift = ShiftState & KeyMod::Shift; bool alt = ShiftState & KeyMod::Alt; bool ctrl = ShiftState & KeyMod::Ctrl; /* If this is a break character, we need to know: */ bool isBreak = scanCode & 0x80u; uint32_t keyCode = scanCode & 0x7fu; switch (esc_code) { case 0x0: { /* printf("esc_code==0\n"); */ switch (scanCode) { case 0xe0: case 0xe1: esc_code = scanCode; continue; default: if (keyCode >= num_scan) { printf("<? 0x0 \\x%x>", keyCode); return; } } break; } case 0xe0: { switch (keyCode) { case 0x2a: /* shift hack used by some keyboards */ /* for extra keys! */ shift = ! shift; case 0x1c: /* kpd-enter */ case 0x1d: /* rctrl */ case 0x35: /* kpd-/ */ case 0x38: /* ralt */ case 0x47: /* home */ case 0x48: /* uparrow */ case 0x49: /* pgup */ case 0x4b: /* left-arrow */ case 0x4d: /* right-arrow */ case 0x4f: /* end */ case 0x50: /* down-arrow */ case 0x51: /* pgdn */ case 0x52: /* insert */ case 0x53: /* del */ esc_code = 0; break; case 0x5b: /* lwindow */ case 0x5c: /* rwindow */ case 0x5d: /* menu */ /* consume these transparently: */ esc_code = 0; return; default: printf("<? 0xe0 \\x%x>", scanCode); esc_code = 0; return; } break; } case 0xe1: { if (keyCode == 0x1d) { esc_code = scanCode; continue; } else { esc_code = 0; printf("<? 0xe1 \\x%x>", scanCode); return; } break; } case 0x1d: { if (keyCode == 0x68) { /* consume transparently */ esc_code = 0; return; } else { printf("<? 0x1d \\x%x>", scanCode); esc_code = 0; return; } break; } default: printf("Unknown escape 0x%x\n", esc_code); break; } /* printf("Key code is %d (0x%x)\n", keyCode, keyCode); */ KeyInfo& ki = key_table[keyCode]; /* printf("<kf=\\x%x,0x%x>", keyCode, ki.flags); */ if ( (ki.flags & KeyMod::IsAlpha) && (ShiftState & KeyMod::AlphaLock) ) shift = !shift; if ( (ki.flags & KeyMod::IsPad) && (ShiftState & KeyMod::NumLock) ) shift = !shift; uint32_t ndx = (shift ? 1 : 0) + (ctrl ? 2 : 0); uint32_t ascii = ki.value[ndx]; /* keep track of shift, ctrl, alt */ if (isBreak) ShiftState &= ~ (ki.flags & 0xf0u); else { ShiftState |= (ki.flags & 0xf0u); } /* keep track of the various lock keys on the break, not the * keypress - the break doesn't repeat. These are toggles, thus * the XOR: */ if (isBreak && (ki.flags & 0xfu)) { ShiftState ^= (ki.flags & 0xfu); UpdateKbdLeds(); } if (isBreak || ascii >= NOP) return; /* Check for three-fingered salute: */ if ( keyCode == 0x53u && ctrl && alt) Reboot();#ifdef OPTION_DDB /* Check for kernel debugger: */ if ( keyCode == 0x20u && ctrl && alt)/* 20u == 'd' */ Debugger();#endif#if 1 if (kbdBufferTop == KbdBufferSz) fatal("Keyboard buffer overflow\n"); else kbdBuffer[kbdBufferTop++] = ascii; KbdSleepQ.WakeAll();#else /* If it's a printing character, just print it, otherwise * print the hex of it: */ if (ascii == '\r') printf("\n"); else if (ascii >= ' ' && ascii < 127) printf("%c", ascii); else printf("\\x%x", ascii);#endif } }}voidKeyboardInterrupt(fixregs_t *sa){ uint32_t irq = IRQ_FROM_EXCEPTION(sa->ExceptNo); assert(irq == 1); if (kbd_polling) return; FetchInputFromKeyboard(); IRQ::Enable(irq);}intGetCharFromKbd(){ assert(kbdBufferTop < KbdBufferSz); if (kbd_polling) { while (kbdBufferTop == 0) FetchInputFromKeyboard(); } if (kbdBufferTop == 0) return 0; int c = kbdBuffer[0]; for (uint32_t i = 1; i < kbdBufferTop; i++) kbdBuffer[i-1] = kbdBuffer[i]; kbdBufferTop --; return c;}static boolProbe(struct AutoConf* /* ac */) {#ifdef OPTION_KBD IRQ::SetHandler(IRQ386::Keyboard, KeyboardInterrupt); printf("Set up keyboard interrupt handler!\n");#else printf("Keyboard included for debugger; handler is unbound!\n");#endif return true;}static boolAttach(struct AutoConf* /* ac */) {#if 0 /* Establish initial keyboard state visibly: */ UpdateKbdLeds();#endif return true;}#include <eros/Invoke.h>#include <eros/KeyboardKey.h>voidKeyboardKey(Invocation& inv){ switch (inv.entry.code) { case OC_Keyboard_Get: { uint32_t len = inv.entry.w1; if (KbdBufferSz < len) len = KbdBufferSz; assert(kbdBufferTop < KbdBufferSz); if (kbdBufferTop < len) { Thread::Current()->SleepOn(KbdSleepQ); Thread::Current()->Yield(); }#ifndef OPTION_PURE_EXIT_STRINGS inv.invokee->SetupExitString(inv, len);#endif COMMIT_POINT(); inv.CopyOut(len, kbdBuffer); for (uint32_t i = len; i < kbdBufferTop; i++) kbdBuffer[i-len] = kbdBuffer[i]; kbdBufferTop -= len; /* Transfer the order code: */ inv.exit.code = RC_OK; return; } default: COMMIT_POINT(); inv.exit.code = RC_UnknownRequest; return; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -