📄 kbdriver.c
字号:
/*****************************************************************************
Keyboard driver/interrupt handler demo code.
This code is public domain (no copyright).
You can do whatever you want with it.
Scancode set 3 is very nice:
- one-byte make code for each key
- break code is the make code preceded by F0h
- each key can be programmed individually to repeat or not,
and to generate a break code or not
Unfortunately, not all keyboards support set 3.
Scancodes set 2 and 1 are crap:
- keys have scancodes of different lengths: 01 for Esc,
E038 for right Alt, and E11D45E19DC5 for Pause in set 1
- some keys produce different scancodes depending on
the internal num lock state of the keyboard:
Insert, Home, PageUp, Delete, End, PageDown,
the arrow keys, and the Windows and Menu keys
- the 8042 keyboard controller chip does optional translation
of set 2 scancodes (AT) to set 1 (XT), so that's one more
thing to worry about
To give you an idea of what a mess this is,
look at the set 2 scancodes for the Insert key:
8042 internal
XT-to-AT Num Lock make repeat break
conversion state code code code
---------- -------- ----- ------ -----
off off E070 E070 E0F070
off ON E012E070 E070 E0F070E0F012
ON off E052 E052 E0D2
ON ON E02AE052 E052 E0D2E0AA
Naturally, only set 2 seems to be supported by all keyboards.
xxx - trouble with this code: when keyboard internal num lock is on,
the E0AA or E0F012 appears BEFORE the NEXT MAKE code,
instead of AFTER the CURRENT BREAK code
*****************************************************************************/
#include <arch.h> /* inportb(), outportb() */
/* this will change if you reprogram the 8259 chips to route
IRQs to non-reserved INTs (a pmode OS should do this) */
#define KBD_VECT_NUM 9
#define INTERRUPT /* nothing */
/*----------------------------------------------------------------------------
CIRCULAR QUEUES
----------------------------------------------------------------------------*/
typedef struct
{
unsigned char *data;
unsigned size, in_ptr, out_ptr;
} queue_t;
/*****************************************************************************
*****************************************************************************/
static int inq(queue_t *q, unsigned data)
{
unsigned temp;
temp = q->in_ptr + 1;
if(temp >= q->size)
temp = 0;
/* if in_ptr reaches out_ptr, the queue is full */
if(temp == q->out_ptr)
return -1;
q->data[q->in_ptr] = data;
q->in_ptr = temp;
return 0;
}
/*****************************************************************************
*****************************************************************************/
static int deq(queue_t *q, unsigned char *data)
{
/* if out_ptr reaches in_ptr, the queue is empty */
if(q->out_ptr == q->in_ptr)
return -1;
*data = q->data[q->out_ptr++];
if(q->out_ptr >= q->size)
q->out_ptr = 0;
return 0;
}
/*****************************************************************************
*****************************************************************************/
static int empty(queue_t *q)
{
return q->out_ptr == q->in_ptr;
}
/*----------------------------------------------------------------------------
LOW-LEVEL KEYBOARD DRIVER
----------------------------------------------------------------------------*/
#define BUF_SIZE 64
static unsigned char g_kbd_buf[BUF_SIZE];
static queue_t g_queue =
{
g_kbd_buf, BUF_SIZE, 0, 0
};
/*****************************************************************************
*****************************************************************************/
void INTERRUPT kbd_irq(void)
{
unsigned scan_code;
/* read I/O port 60h to reset interrupt at 8042 keyboard controller chip */
scan_code = readchar();
/* put scancode in queue */
(void)inq(&g_queue, scan_code);
/* reset interrupt at 8259 interrupt controller chip */
outb_p(0x20, 0x20);
}
/*****************************************************************************
*****************************************************************************/
/*****************************************************************************
*****************************************************************************/
static void write_kbd(unsigned adr, unsigned data)
{
unsigned long timeout;
unsigned stat;
for(timeout = 500000L; timeout != 0; timeout--)
{
stat = inb_p(0x64);
/* loop until 8042 input buffer empty */
if((stat & 0x02) == 0)
break;
}
if(timeout == 0)
{
kprintf("write_kbd: timeout\n");
return;
}
outb_p(adr, data);
}
/*****************************************************************************
*****************************************************************************/
#if 0
static int read_kbd(void)
{
unsigned long timeout;
unsigned stat, data;
for(timeout = 500000L; timeout != 0; timeout--)
{
stat = inb_p(0x64);
/* loop until 8042 output buffer full */
if(stat & 0x01)
{
data = inb_p(0x60);
/* loop if parity error or receive timeout */
if((stat & 0xC0) == 0)
return data;
}
}
/* kprintf("read_kbd: timeout\n"); */
return -1;
}
static int write_kbd_await_ack(unsigned val)
{
int got;
write_kbd(0x60, val);
got = read_kbd();
if(got != 0xFA)
{
kprintf("write_kbd_await_ack: expected (0xFA)");
newline();
return -1;
}
return 0;
}
/*****************************************************************************
*****************************************************************************/
static int init_kbd(unsigned ss, unsigned typematic, unsigned xlat)
{
// kprintf("flushing keyboard output\n");
while(read_kbd() != -1)
/* nothing */;
/* disable keyboard before programming it */
//kprintf("disabling keyboard controller\n");
if(write_kbd_await_ack(0xF5) != 0)
return -1;
/* disable PS/2 mouse, set SYS bit, and Enable Keyboard Interrupt... */
write_kbd(0x64, 0x60);
/* ...and either disable or enable AT-to-XT keystroke conversion */
write_kbd(0x60, xlat ? 0x65 : 0x25);
/* program desired scancode set */
//kprintf("programming scancode set "); newline();
if(write_kbd_await_ack(0xF0) != 0)
//return -1;
if(write_kbd_await_ack(ss) != 0)
return -1;
/* we want all keys to return both a make code (when pressed)
and a break code (when released -- scancode set 3 only) */
if(ss == 3)
{
//kprintf("making all keys make-break");
if(write_kbd_await_ack(0xFA) != 0)
return -1;
}
/* set typematic delay and rate */
//kprintf("setting fast typematic mode");
if(write_kbd_await_ack(0xF3) != 0)
return -1;
if(write_kbd_await_ack(typematic) != 0)
return -1;
/* enable keyboard */
//kprintf("enabling keyboard controller\n");
if(write_kbd_await_ack(0xF4) != 0)
return -1;
return 0;
}
#endif
/*----------------------------------------------------------------------------
SCANCODE CONVERSION
----------------------------------------------------------------------------*/
/* "ASCII" values for non-ASCII keys. All of these are user-defined.
function keys: */
#define KEY_F1 0x80
#define KEY_F2 (KEY_F1 + 1)
#define KEY_F3 (KEY_F2 + 1)
#define KEY_F4 (KEY_F3 + 1)
#define KEY_F5 (KEY_F4 + 1)
#define KEY_F6 (KEY_F5 + 1)
#define KEY_F7 (KEY_F6 + 1)
#define KEY_F8 (KEY_F7 + 1)
#define KEY_F9 (KEY_F8 + 1)
#define KEY_F10 (KEY_F9 + 1)
#define KEY_F11 (KEY_F10 + 1)
#define KEY_F12 (KEY_F11 + 1)
/* cursor keys */
#define KEY_INS 0x90
#define KEY_DEL (KEY_INS + 1)
#define KEY_HOME (KEY_DEL + 1)
#define KEY_END (KEY_HOME + 1)
#define KEY_PGUP (KEY_END + 1)
#define KEY_PGDN (KEY_PGUP + 1)
#define KEY_LFT (KEY_PGDN + 1)
#define KEY_UP (KEY_LFT + 1)
#define KEY_DN (KEY_UP + 1)
#define KEY_RT (KEY_DN + 1)
/* print screen/sys rq and pause/break */
#define KEY_PRNT (KEY_RT + 1)
#define KEY_PAUSE (KEY_PRNT + 1)
/* these return a value but they could also act as additional meta keys */
#define KEY_LWIN (KEY_PAUSE + 1)
#define KEY_RWIN (KEY_LWIN + 1)
#define KEY_MENU (KEY_RWIN + 1)
/* "meta bits"
0x0100 is reserved for non-ASCII keys, so start with 0x200 */
#define KBD_META_ALT 0x0200 /* Alt is pressed */
#define KBD_META_CTRL 0x0400 /* Ctrl is pressed */
#define KBD_META_SHIFT 0x0800 /* Shift is pressed */
#define KBD_META_ANY (KBD_META_ALT | KBD_META_CTRL | KBD_META_SHIFT)
#define KBD_META_CAPS 0x1000 /* CapsLock is on */
#define KBD_META_NUM 0x2000 /* NumLock is on */
#define KBD_META_SCRL 0x4000 /* ScrollLock is on */
/*****************************************************************************
*****************************************************************************/
#define RAW1_LEFT_CTRL 0x1D
#define RAW1_LEFT_SHIFT 0x2A
#define RAW1_CAPS_LOCK 0x3A
#define RAW1_LEFT_ALT 0x38
#define RAW1_RIGHT_ALT 0x38 /* same as left */
#define RAW1_RIGHT_CTRL 0x1D /* same as left */
#define RAW1_RIGHT_SHIFT 0x36
#define RAW1_SCROLL_LOCK 0x46
#define RAW1_NUM_LOCK 0x45
#define RAW1_DEL 0x53
static int set1_scancode_to_ascii(unsigned code)
{
static const unsigned char map[] =
{
/* 00 */0, 0x1B, '1', '2', '3', '4', '5', '6',
/* 08 */'7', '8', '9', '0', '-', '=', '\b', '\t',
/* 10 */'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
/* 1Dh is left Ctrl */
/* 18 */'o', 'p', '[', ']', '\n', 0, 'a', 's',
/* 20 */'d', 'f', 'g', 'h', 'j', 'k', 'l', ';',
/* 2Ah is left Shift */
/* 28 */'\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
/* 36h is right Shift */
/* 30 */'b', 'n', 'm', ',', '.', '/', 0, 0,
/* 38h is left Alt, 3Ah is Caps Lock */
/* 38 */0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
/* 45h is Num Lock, 46h is Scroll Lock */
/* 40 */KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0, 0, KEY_HOME,
/* 48 */KEY_UP, KEY_PGUP,'-', KEY_LFT,'5', KEY_RT, '+', KEY_END,
/* 50 */KEY_DN, KEY_PGDN,KEY_INS,KEY_DEL,0, 0, 0, KEY_F11,
/* 58 */KEY_F12
};
static const unsigned char shift_map[] =
{
/* 00 */0, 0x1B, '!', '@', '#', '$', '%', '^',
/* 08 */'&', '*', '(', ')', '_', '+', '\b', '\t',
/* 10 */'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
/* 1Dh is left Ctrl */
/* 18 */'O', 'P', '{', '}', '\n', 0, 'A', 'S',
/* 20 */'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
/* 2Ah is left Shift */
/* 28 */'"', '~', 0, '|', 'Z', 'X', 'C', 'V',
/* 36h is right Shift */
/* 30 */'B', 'N', 'M', '<', '>', '?', 0, 0,
/* 38h is left Alt, 3Ah is Caps Lock */
/* 38 */0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
/* 45h is Num Lock, 46h is Scroll Lock */
/* 40 */KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0, 0, KEY_HOME,
/* 48 */KEY_UP, KEY_PGUP,'-', KEY_LFT,'5', KEY_RT, '+', KEY_END,
/* 50 */KEY_DN, KEY_PGDN,KEY_INS,KEY_DEL,0, 0, 0, KEY_F11,
/* 58 */KEY_F12
};
static unsigned saw_break_code, kbd_status;
/**/
unsigned temp;
/* check for break code (i.e. a key is released) */
if(code >= 0x80)
{
saw_break_code = 1;
code &= 0x7F;
}
/* the only break codes we're interested in are Shift, Ctrl, Alt */
if(saw_break_code)
{
if(code == RAW1_LEFT_ALT || code == RAW1_RIGHT_ALT)
kbd_status &= ~KBD_META_ALT;
else if(code == RAW1_LEFT_CTRL || code == RAW1_RIGHT_CTRL)
kbd_status &= ~KBD_META_CTRL;
else if(code == RAW1_LEFT_SHIFT || code == RAW1_RIGHT_SHIFT)
kbd_status &= ~KBD_META_SHIFT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -