1 /*
2 * Copyright (c) 2009 Corey Tabaka
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <platform/keyboard.h>
9
10 #include <sys/types.h>
11 #include <lk/err.h>
12 #include <lk/reg.h>
13 #include <lk/trace.h>
14 #include <lk/debug.h>
15 #include <kernel/thread.h>
16 #include <platform.h>
17 #include <platform/interrupts.h>
18 #include <platform/console.h>
19 #include <platform/timer.h>
20 #include <platform/pc.h>
21 #include "platform_p.h"
22 #include <arch/x86.h>
23 #include <lib/cbuf.h>
24
i8042_read_data(void)25 static inline int i8042_read_data(void) {
26 return inp(I8042_DATA_REG);
27 }
28
i8042_read_status(void)29 static inline int i8042_read_status(void) {
30 return inp(I8042_STATUS_REG);
31 }
32
i8042_write_data(int val)33 static inline void i8042_write_data(int val) {
34 outp(I8042_DATA_REG, val);
35 }
36
i8042_write_command(int val)37 static inline void i8042_write_command(int val) {
38 outp(I8042_COMMAND_REG, val);
39 }
40
41 /*
42 * timeout in milliseconds
43 */
44 #define I8042_CTL_TIMEOUT 500
45
46 /*
47 * status register bits
48 */
49 #define I8042_STR_PARITY 0x80
50 #define I8042_STR_TIMEOUT 0x40
51 #define I8042_STR_AUXDATA 0x20
52 #define I8042_STR_KEYLOCK 0x10
53 #define I8042_STR_CMDDAT 0x08
54 #define I8042_STR_MUXERR 0x04
55 #define I8042_STR_IBF 0x02
56 #define I8042_STR_OBF 0x01
57
58 /*
59 * control register bits
60 */
61 #define I8042_CTR_KBDINT 0x01
62 #define I8042_CTR_AUXINT 0x02
63 #define I8042_CTR_IGNKEYLK 0x08
64 #define I8042_CTR_KBDDIS 0x10
65 #define I8042_CTR_AUXDIS 0x20
66 #define I8042_CTR_XLATE 0x40
67
68 /*
69 * commands
70 */
71 #define I8042_CMD_CTL_RCTR 0x0120
72 #define I8042_CMD_CTL_WCTR 0x1060
73 #define I8042_CMD_CTL_TEST 0x01aa
74
75 #define I8042_CMD_KBD_DIS 0x00ad
76 #define I8042_CMD_KBD_EN 0x00ae
77 #define I8042_CMD_KBD_TEST 0x01ab
78 #define I8042_CMD_KBD_MODE 0x01f0
79
80 /*
81 * used for flushing buffers. the i8042 internal buffer shoudn't exceed this.
82 */
83 #define I8042_BUFFER_LENGTH 32
84
delay(lk_time_t delay)85 static inline void delay(lk_time_t delay) {
86 lk_time_t start = current_time();
87
88 while (start + delay > current_time());
89 }
90
91 /* scancodes we want to do something with that don't translate via table */
92 #define SCANCODE_LSHIFT 0x2a
93 #define SCANCODE_RSHIFT 0x36
94
95 /* scancode translation tables */
96 static const int KeyCodeSingleLower[] = {
97 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
98 -1, -1, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', // 0
99 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\n', -1, 'a', 's', // 1
100 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`', -1,'\\', 'z', 'x', 'c', 'v', // 2
101 'b', 'n', 'm', ',', '.', '/', -1, '*', -1, ' ', -1, -1, -1, -1, -1, -1, // 3
102 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
103 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
104 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
105 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
106 };
107
108 static const int KeyCodeMultiLower[] = {
109 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
110 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
111 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1
112 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2
113 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 3
114 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
115 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
116 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
117 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
118 };
119
120 static const int KeyCodeSingleUpper[] = {
121 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
122 -1, -1, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', // 0
123 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\n', -1, 'A', 'S', // 1
124 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', -1, '|', 'Z', 'X', 'C', 'V', // 2
125 'B', 'N', 'M', '<', '>', '?', -1, '*', -1, ' ', -1, -1, -1, -1, -1, -1, // 3
126 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
130 };
131
132 static const int KeyCodeMultiUpper[] = {
133 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
134 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
135 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1
136 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2
137 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 3
138 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
139 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
140 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
141 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
142 };
143
144 /*
145 * state key flags
146 */
147 static bool key_lshift;
148 static bool key_rshift;
149
150 static cbuf_t *key_buf;
151
i8042_process_scode(uint8_t scode,unsigned int flags)152 static void i8042_process_scode(uint8_t scode, unsigned int flags) {
153 static int lastCode = 0;
154 int keyCode;
155 uint8_t keyUpBit;
156
157 bool multi = lastCode == 0xe0;
158
159 // save the key up event bit
160 keyUpBit = scode & 0x80;
161 scode &= 0x7f;
162
163 if (scode == SCANCODE_LSHIFT) {
164 key_lshift = !keyUpBit;
165 }
166
167 if (scode == SCANCODE_RSHIFT) {
168 key_rshift = !keyUpBit;
169 }
170
171 if (key_lshift || key_rshift) {
172 keyCode = multi ? KeyCodeMultiUpper[scode] : KeyCodeSingleUpper[scode];
173 } else {
174 keyCode = multi ? KeyCodeMultiLower[scode] : KeyCodeSingleLower[scode];
175 }
176
177 /*printf_xy(71, 3, BLUE, "%02x%02x %c %c%c", multi ? lastCode : 0, scode,
178 keyCode != -1 ? (char) keyCode : ' ', key_lshift ? 'L' : ' ',
179 key_rshift ? 'R' : ' ');*/
180
181 if (keyCode != -1 && !keyUpBit) {
182 char c = (char) keyCode;
183 cbuf_write_char(key_buf, c, false);
184 }
185
186 // update the last received code
187 lastCode = scode;
188 }
189
i8042_wait_read(void)190 static int i8042_wait_read(void) {
191 int i = 0;
192 while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
193 delay(1);
194 i++;
195 }
196 return -(i == I8042_CTL_TIMEOUT);
197 }
198
i8042_wait_write(void)199 static int i8042_wait_write(void) {
200 int i = 0;
201 while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
202 delay(1);
203 i++;
204 }
205 return -(i == I8042_CTL_TIMEOUT);
206 }
207
i8042_flush(void)208 static int i8042_flush(void) {
209 unsigned char data __UNUSED;
210 int i = 0;
211
212 //enter_critical_section();
213
214 while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) {
215 delay(1);
216 data = i8042_read_data();
217 }
218
219 //exit_critical_section();
220
221 return i;
222 }
223
i8042_command(uint8_t * param,int command)224 static int i8042_command(uint8_t *param, int command) {
225 int retval = 0, i = 0;
226
227 //enter_critical_section();
228
229 retval = i8042_wait_write();
230 if (!retval) {
231 i8042_write_command(command & 0xff);
232 }
233
234 if (!retval) {
235 for (i = 0; i < ((command >> 12) & 0xf); i++) {
236 if ((retval = i8042_wait_write())) {
237 break;
238 }
239
240 i8042_write_data(param[i]);
241 }
242 }
243
244 if (!retval) {
245 for (i = 0; i < ((command & 0xf0) >> 8); i++) {
246 if ((retval = i8042_wait_read())) {
247 break;
248 }
249
250 if (i8042_read_status() & I8042_STR_AUXDATA) {
251 param[i] = ~i8042_read_data();
252 } else {
253 param[i] = i8042_read_data();
254 }
255 }
256 }
257
258 //exit_critical_section();
259
260 return retval;
261 }
262
i8042_interrupt(void * arg)263 static enum handler_return i8042_interrupt(void *arg) {
264 uint8_t str, data = 0;
265 bool resched = false;
266
267 //enter_critical_section();
268 str = i8042_read_status();
269 if (str & I8042_STR_OBF) {
270 data = i8042_read_data();
271 }
272 //exit_critical_section();
273
274 if (str & I8042_STR_OBF) {
275 i8042_process_scode(data,
276 ((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) |
277 ((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0));
278 resched = true;
279 }
280
281 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
282 }
283
platform_read_key(char * c)284 int platform_read_key(char *c) {
285 ssize_t len;
286
287 len = cbuf_read_char(key_buf, c, true);
288 return len;
289 }
290
platform_init_keyboard(cbuf_t * buffer)291 void platform_init_keyboard(cbuf_t *buffer) {
292 uint8_t ctr;
293
294 key_buf = buffer;
295
296 i8042_flush();
297
298 ctr = 0;
299 if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
300 dprintf(SPEW, "Failed to read CTR while initializing i8042\n");
301 return;
302 }
303
304 // turn on translation
305 ctr |= I8042_CTR_XLATE;
306
307 // enable keyboard and keyboard irq
308 ctr &= ~I8042_CTR_KBDDIS;
309 ctr |= I8042_CTR_KBDINT;
310
311 if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
312 dprintf(SPEW, "Failed to write CTR while initializing i8042\n");
313 return;
314 }
315
316 register_int_handler(INT_KEYBOARD, &i8042_interrupt, NULL);
317 unmask_interrupt(INT_KEYBOARD);
318
319 i8042_interrupt(NULL);
320 }
321