📄 kbd.c
字号:
/*****************************************************************************
PC KEYBOARD
EXPORTS:
void kbd_irq(void);
void init_kbd(void);
*****************************************************************************/
#include <conio.h> /* KEY_... */
#include <krnl.h>
#include <x86.h> /* inportb(), outportb() */
/* IMPORTS
from STARTUP.ASM */
void halt(void);
/* from VIDEO.C */
extern console_t _vc[];
extern volatile console_t *_curr_vc;
void select_vc(unsigned which_con);
void putch(console_t *con, unsigned char c);
/* from MAIN.C */
void kprintf(const char *fmt, ...);
void wake_up(wait_queue_t *queue);
/* "raw" set 1 scancodes from PC keyboard. Keyboard info here:
http://www.execpc.com/~geezer/osd/kbd */
#define RAW_LEFT_CTRL 0x1D
#define RAW_LEFT_SHIFT 0x2A
#define RAW_CAPS_LOCK 0x3A
#define RAW_LEFT_ALT 0x38
#define RAW_RIGHT_ALT 0x38 /* same as left */
#define RAW_RIGHT_CTRL 0x1D /* same as left */
#define RAW_RIGHT_SHIFT 0x36
#define RAW_SCROLL_LOCK 0x46
#define RAW_NUM_LOCK 0x45
#define KBD_BUF_SIZE 512
/*****************************************************************************
*****************************************************************************/
static void reboot(void)
{
unsigned char temp;
disable();
/* flush the keyboard controller */
do
{
temp = inportb(0x64);
if((temp & 0x01) != 0)
{
(void)inportb(0x60);
continue;
}
} while((temp & 0x02) != 0);
/* pulse the CPU reset line */
outportb(0x64, 0xFE);
/* ...and if that didn't work, just halt */
halt();
}
/*****************************************************************************
name: inq
action: tries to add data (a byte) to queue
returns:-1 if queue full
0 if success
*****************************************************************************/
static int inq(queue_t *q, unsigned char data)
{
unsigned temp;
temp = q->in_ptr + 1;
if(temp >= q->size)
temp = 0;
/* if in_ptr reaches out_base, the queue is full */
if(temp == q->out_base)
return -1;
q->data[q->in_ptr] = data;
q->in_ptr = temp;
return 0;
}
/*****************************************************************************
name: write_kbd
action: writes data to 8048 keyboard MCU (adr=0x60)
or 8042 controller MCU (adr=0x64)
*****************************************************************************/
static void write_kbd(unsigned adr, unsigned char data)
{
unsigned long timeout;
unsigned char stat;
/* Linux code didn't have a timeout here... */
for(timeout = 500000L; timeout != 0; timeout--)
{
stat = inportb(0x64);
/* loop until 8042 input buffer empty */
if((stat & 0x02) == 0)
break;
}
if(timeout != 0)
outportb(adr, data);
}
/*****************************************************************************
name: convert
action: converts raw scancodes to pseudo-ASCII
returns:0 if nothing to return, else 8-bit "ASCII" value
*****************************************************************************/
static unsigned short convert(console_t *con, unsigned char 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 unsigned short kbd_status;
unsigned short temp;
/* check for break code (i.e. a key is released) */
if(code >= 0x80)
{
con->saw_break_code = 1;
code &= 0x7F;
}
/* the only break codes we're interested in are Shift, Ctrl, Alt */
if(con->saw_break_code)
{
if(code == RAW_LEFT_ALT || code == RAW_RIGHT_ALT)
kbd_status &= ~KBD_META_ALT;
else if(code == RAW_LEFT_CTRL || code == RAW_RIGHT_CTRL)
kbd_status &= ~KBD_META_CTRL;
else if(code == RAW_LEFT_SHIFT || code == RAW_RIGHT_SHIFT)
kbd_status &= ~KBD_META_SHIFT;
con->saw_break_code = 0;
return 0;
}
/* it's a make code: check the "meta" keys, as above */
if(code == RAW_LEFT_ALT || code == RAW_RIGHT_ALT)
{
kbd_status |= KBD_META_ALT;
return 0;
}
if(code == RAW_LEFT_CTRL || code == RAW_RIGHT_CTRL)
{
kbd_status |= KBD_META_CTRL;
return 0;
}
if(code == RAW_LEFT_SHIFT || code == RAW_RIGHT_SHIFT)
{
kbd_status |= KBD_META_SHIFT;
return 0;
}
/* Scroll Lock, Num Lock, and Caps Lock set the LEDs. These keys
have on-off (toggle or XOR) action, instead of momentary action */
if(code == RAW_SCROLL_LOCK)
{
kbd_status ^= KBD_META_SCRL;
goto LEDS;
}
if(code == RAW_NUM_LOCK)
{
kbd_status ^= KBD_META_NUM;
goto LEDS;
}
if(code == RAW_CAPS_LOCK)
{
kbd_status ^= KBD_META_CAPS;
LEDS: write_kbd(0x60, 0xED); /* "set LEDs" command */
temp = 0;
if(kbd_status & KBD_META_SCRL)
temp |= 1;
if(kbd_status & KBD_META_NUM)
temp |= 2;
if(kbd_status & KBD_META_CAPS)
temp |= 4;
write_kbd(0x60, temp); /* bottom 3 bits set LEDs */
return 0;
}
/* ignore invalid scan codes */
if(code >= sizeof(map) / sizeof(map[0]))
return 0;
/* convert raw scancode in code to unshifted ASCII in temp */
temp = map[code];
/* defective keyboard? non-US keyboard? more than 104 keys? */
if(temp == 0)
return temp;
/* handle the three-finger salute */
if((kbd_status & KBD_META_CTRL) && (kbd_status & KBD_META_ALT) &&
(temp == KEY_DEL))
{
kprintf("\n""\x1B[42;37;1m""*** rebooting!");
reboot();
}
/* I really don't know what to do yet with Alt, Ctrl, etc. -- punt */
return temp;
}
/*****************************************************************************
done:
8=BS 13=CR
to do:
4=EOF 7=BEL 9=TAB 10=LF 12=FF ?=CAN (erase line)
*****************************************************************************/
static void do_console(unsigned char key)
{
unsigned temp;
queue_t *q;
q = &_curr_vc->keystrokes;
if(_curr_vc->unbuffered)
{
if(inq(q, key) != 0)
/* full queue, beep or something */;
/* advance in_base and wake up waiting sys_read()
continuously, with each keystroke */
q->in_base = q->in_ptr;
wake_up(&_curr_vc->wait_queue);
}
else
{
/* backspace */
if(key == 8)
{
/* if in_ptr equals in_base, the input side of the queue is empty */
if(q->in_ptr != q->in_base)
{
if(q->in_ptr == 0)
q->in_ptr = q->size - 1;
else
q->in_ptr--;
putch(_curr_vc, key);
putch(_curr_vc, ' ');
putch(_curr_vc, key);
}
}
else if(key == '\n')
{
if(inq(q, key) || inq(q, '\0'))
return;/* xxx - should not happen */
putch(_curr_vc, key);
putch(_curr_vc, '\r');
/* advance in_base and wake up waiting sys_read()
only when Return is pressed */
q->in_base = q->in_ptr;
wake_up(&_curr_vc->wait_queue);
}
else if(key >= ' ')
{
/* need room for 3 bytes (key, and CR and NUL that are stored by Enter) */
temp = q->in_ptr + 1;
if(temp >= q->size)
temp = 0;
/* if in_ptr reaches out_base, the queue is full */
if(temp == q->out_base)
return;
temp = q->in_ptr + 1;
if(temp >= q->size)
temp = 0;
if(temp == q->out_base)
return;
temp = q->in_ptr + 1;
if(temp >= q->size)
temp = 0;
if(temp == q->out_base)
return;
if(inq(q, key))
return;/* xxx - should not happen */
putch(_curr_vc, key);
}
}
}
/*****************************************************************************
*****************************************************************************/
void kbd_irq(void)
{
unsigned short temp;
/* get scancode from port 0x60 */
temp = inportb(0x60);
/* convert scancode to pseudo-ASCII */
temp = convert(_curr_vc, temp);
/* if it's F1, F2 etc. switch to the appropriate virtual console */
switch(temp)
{
case KEY_F1:
temp = 0;
goto SWITCH_VC;
case KEY_F2:
temp = 1;
goto SWITCH_VC;
case KEY_F3:
temp = 2;
goto SWITCH_VC;
case KEY_F4:
temp = 3;
goto SWITCH_VC;
case KEY_F5:
temp = 4;
goto SWITCH_VC;
case KEY_F6:
temp = 5;
goto SWITCH_VC;
case KEY_F7:
temp = 6;
goto SWITCH_VC;
case KEY_F8:
temp = 7;
goto SWITCH_VC;
case KEY_F9:
temp = 8;
goto SWITCH_VC;
case KEY_F10:
temp = 9;
goto SWITCH_VC;
case KEY_F11:
temp = 10;
goto SWITCH_VC;
case KEY_F12:
temp = 11;
SWITCH_VC:
select_vc(temp);
break;
/* convert() ate it */
case 0:
break;
default:
do_console(temp);
break;
}
}
/*****************************************************************************
*****************************************************************************/
DISCARDABLE_CODE(void init_kbd(void))
{
static unsigned char buffers[KBD_BUF_SIZE * MAX_VC];
int temp;
for(temp = 0; temp < MAX_VC; temp++)
{
_vc[temp].keystrokes.data = buffers + KBD_BUF_SIZE * temp;
_vc[temp].keystrokes.size = KBD_BUF_SIZE;
}
kprintf("init_kbd: %u buffers, %u bytes each\n",
MAX_VC, KBD_BUF_SIZE);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -