summaryrefslogtreecommitdiffstats
path: root/src/core/pc_kbd.c
blob: 42df755b510f5ee2462447699a699efe078eace8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/* Minimal polling PC keyboard driver
 * - No interrupt
 * - No LED
 * - No special keys
 *
 * still Enough For Me to type a filename.
 *
 * 2003-07 by SONE Takesh
 * 2004-04 moved by LYH From filo to Etherboot
 *		yhlu@tyan.com
 */

#include <ipxe/io.h>
#include <ipxe/console.h>

static char key_map[][128] = {
    {
	"\0\x1b""1234567890-=\b\t"
	"qwertyuiop[]\r\0as"
	"dfghjkl;'`\0\\zxcv"
	"bnm,./\0*\0 \0\0\0\0\0\0"
	"\0\0\0\0\0\0\0""789-456+1"
	"230."
    },{
	"\0\x1b""!@#$%^&*()_+\b\t"
	"QWERTYUIOP{}\r\0AS"
	"DFGHJKL:\"~\0|ZXCV"
	"BNM<>?\0\0\0 \0\0\0\0\0\0"
	"\0\0\0\0\0\0\0""789-456+1"
	"230."
    }
};

static int cur_scan;
static unsigned int shift_state;
#define SHIFT 1
#define CONTROL 2
#define CAPS 4

static int get_scancode(void)
{
    int scan;

    if ((inb(0x64) & 1) == 0)
	return 0;
    scan = inb(0x60);

    switch (scan) {
    case 0x2a:
    case 0x36:
	shift_state |= SHIFT;
	break;
    case 0xaa:
    case 0xb6:
	shift_state &= ~SHIFT;
	break;
    case 0x1d:
	shift_state |= CONTROL;
	break;
    case 0x9d:
	shift_state &= ~CONTROL;
	break;
    case 0x3a:
	shift_state ^= CAPS;
	break;
    }

    if (scan & 0x80)
	return 0; /* ignore break code or 0xe0 etc! */
    return scan;
}

static int kbd_havekey(void)
{
    if (!cur_scan)
	cur_scan = get_scancode();
    return cur_scan != 0;
}

static int kbd_ischar(void)
{
    if (!kbd_havekey())
	return 0;
    if (!key_map[shift_state & SHIFT][cur_scan]) {
	cur_scan = 0;
	return 0;
    }
    return 1;
}

static int kbd_getc(void)
{
    int c;

    while (!kbd_ischar())
	;
    c = key_map[shift_state & SHIFT][cur_scan];
    if (shift_state & (CONTROL | CAPS)) {
	if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
	    if (shift_state & CONTROL)
		c &= 0x1f;
	    else if (shift_state & CAPS)
		c ^= ('A' ^ 'a');
	}
    }
    cur_scan = 0;
    return c;
}

struct console_driver pc_kbd_console __console_driver = {
	.getchar = kbd_getc,
};