-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkbd.c
executable file
·213 lines (186 loc) · 5.39 KB
/
kbd.c
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include "devices/kbd.h"
#include <ctype.h>
#include <debug.h>
#include <stdio.h>
#include <string.h>
#include "devices/input.h"
#include "devices/shutdown.h"
#include "threads/interrupt.h"
#include "threads/io.h"
/* Keyboard data register port. */
#define DATA_REG 0x60
/* Current state of shift keys.
True if depressed, false otherwise. */
static bool left_shift, right_shift; /* Left and right Shift keys. */
static bool left_alt, right_alt; /* Left and right Alt keys. */
static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */
/* Status of Caps Lock.
True when on, false when off. */
static bool caps_lock;
/* Number of keys pressed. */
static int64_t key_cnt;
static intr_handler_func keyboard_interrupt;
/* Initializes the keyboard. */
void
kbd_init (void)
{
intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
}
/* Prints keyboard statistics. */
void
kbd_print_stats (void)
{
printf ("Keyboard: %lld keys pressed\n", key_cnt);
}
/* Maps a set of contiguous scancodes into characters. */
struct keymap
{
uint8_t first_scancode; /* First scancode. */
const char *chars; /* chars[0] has scancode first_scancode,
chars[1] has scancode first_scancode + 1,
and so on to the end of the string. */
};
/* Keys that produce the same characters regardless of whether
the Shift keys are down. Case of letters is an exception
that we handle elsewhere. */
static const struct keymap invariant_keymap[] =
{
{0x01, "\033"}, /* Escape. */
{0x0e, "\b"},
{0x0f, "\tQWERTYUIOP"},
{0x1c, "\r"},
{0x1e, "ASDFGHJKL"},
{0x2c, "ZXCVBNM"},
{0x37, "*"},
{0x39, " "},
{0x53, "\177"}, /* Delete. */
{0, NULL},
};
/* Characters for keys pressed without Shift, for those keys
where it matters. */
static const struct keymap unshifted_keymap[] =
{
{0x02, "1234567890-="},
{0x1a, "[]"},
{0x27, ";'`"},
{0x2b, "\\"},
{0x33, ",./"},
{0, NULL},
};
/* Characters for keys pressed with Shift, for those keys where
it matters. */
static const struct keymap shifted_keymap[] =
{
{0x02, "!@#$%^&*()_+"},
{0x1a, "{}"},
{0x27, ":\"~"},
{0x2b, "|"},
{0x33, "<>?"},
{0, NULL},
};
static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
static void
keyboard_interrupt (struct intr_frame *args UNUSED)
{
/* Status of shift keys. */
bool shift = left_shift || right_shift;
bool alt = left_alt || right_alt;
bool ctrl = left_ctrl || right_ctrl;
/* Keyboard scancode. */
unsigned code;
/* False if key pressed, true if key released. */
bool release;
/* Character that corresponds to `code'. */
uint8_t c;
/* Read scancode, including second byte if prefix code. */
code = inb (DATA_REG);
if (code == 0xe0)
code = (code << 8) | inb (DATA_REG);
/* Bit 0x80 distinguishes key press from key release
(even if there's a prefix). */
release = (code & 0x80) != 0;
code &= ~0x80u;
/* Interpret key. */
if (code == 0x3a)
{
/* Caps Lock. */
if (!release)
caps_lock = !caps_lock;
}
else if (map_key (invariant_keymap, code, &c)
|| (!shift && map_key (unshifted_keymap, code, &c))
|| (shift && map_key (shifted_keymap, code, &c)))
{
/* Ordinary character. */
if (!release)
{
/* Reboot if Ctrl+Alt+Del pressed. */
if (c == 0177 && ctrl && alt)
shutdown_reboot ();
/* Handle Ctrl, Shift.
Note that Ctrl overrides Shift. */
if (ctrl && c >= 0x40 && c < 0x60)
{
/* A is 0x41, Ctrl+A is 0x01, etc. */
c -= 0x40;
}
else if (shift == caps_lock)
c = tolower (c);
/* Handle Alt by setting the high bit.
This 0x80 is unrelated to the one used to
distinguish key press from key release. */
if (alt)
c += 0x80;
/* Append to keyboard buffer. */
if (!input_full ())
{
key_cnt++;
input_putc (c);
}
}
}
else
{
/* Maps a keycode into a shift state variable. */
struct shift_key
{
unsigned scancode;
bool *state_var;
};
/* Table of shift keys. */
static const struct shift_key shift_keys[] =
{
{ 0x2a, &left_shift},
{ 0x36, &right_shift},
{ 0x38, &left_alt},
{0xe038, &right_alt},
{ 0x1d, &left_ctrl},
{0xe01d, &right_ctrl},
{0, NULL},
};
const struct shift_key *key;
/* Scan the table. */
for (key = shift_keys; key->scancode != 0; key++)
if (key->scancode == code)
{
*key->state_var = !release;
break;
}
}
}
/* Scans the array of keymaps K for SCANCODE.
If found, sets *C to the corresponding character and returns
true.
If not found, returns false and C is ignored. */
static bool
map_key (const struct keymap k[], unsigned scancode, uint8_t *c)
{
for (; k->first_scancode != 0; k++)
if (scancode >= k->first_scancode
&& scancode < k->first_scancode + strlen (k->chars))
{
*c = k->chars[scancode - k->first_scancode];
return true;
}
return false;
}