blob: bb057b625e4c2d7d3e44e8e24a96c79df628b491 [file] [log] [blame]
Corey Tabaka84697242009-03-26 02:32:01 -04001/*
2 * Copyright (c) 2009 Corey Tabaka
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <sys/types.h>
24#include <err.h>
25#include <reg.h>
26#include <debug.h>
27#include <kernel/thread.h>
28#include <platform.h>
29#include <platform/interrupts.h>
30#include <platform/console.h>
31#include <platform/timer.h>
32#include <platform/x86.h>
33#include "platform_p.h"
34#include <arch/x86.h>
35
36static inline int i8042_read_data(void)
37{
38 return inp(I8042_DATA_REG);
39}
40
41static inline int i8042_read_status(void)
42{
43 return inp(I8042_STATUS_REG);
44}
45
46static inline void i8042_write_data(int val)
47{
48 outp(I8042_DATA_REG, val);
49}
50
51static inline void i8042_write_command(int val)
52{
53 outp(I8042_COMMAND_REG, val);
54}
55
56/*
57 * timeout in milliseconds
58 */
59#define I8042_CTL_TIMEOUT 500
60
61/*
62 * status register bits
63 */
64#define I8042_STR_PARITY 0x80
65#define I8042_STR_TIMEOUT 0x40
66#define I8042_STR_AUXDATA 0x20
67#define I8042_STR_KEYLOCK 0x10
68#define I8042_STR_CMDDAT 0x08
69#define I8042_STR_MUXERR 0x04
70#define I8042_STR_IBF 0x02
71#define I8042_STR_OBF 0x01
72
73/*
74 * control register bits
75 */
76#define I8042_CTR_KBDINT 0x01
77#define I8042_CTR_AUXINT 0x02
78#define I8042_CTR_IGNKEYLK 0x08
79#define I8042_CTR_KBDDIS 0x10
80#define I8042_CTR_AUXDIS 0x20
81#define I8042_CTR_XLATE 0x40
82
83/*
84 * commands
85 */
86#define I8042_CMD_CTL_RCTR 0x0120
87#define I8042_CMD_CTL_WCTR 0x1060
88#define I8042_CMD_CTL_TEST 0x01aa
89
90#define I8042_CMD_KBD_DIS 0x00ad
91#define I8042_CMD_KBD_EN 0x00ae
92#define I8042_CMD_KBD_TEST 0x01ab
93#define I8042_CMD_KBD_MODE 0x01f0
94
95/*
96 * used for flushing buffers. the i8042 internal buffer shoudn't exceed this.
97 */
98#define I8042_BUFFER_LENGTH 32
99
100static inline void delay(time_t delay) {
101 bigtime_t start = current_time();
102
103 while (start + delay > current_time());
104}
105
106/* scancodes we want to do something with that don't translate via table */
107#define SCANCODE_LSHIFT 0x2a
108#define SCANCODE_RSHIFT 0x36
109
110/* scancode translation tables */
111static const int KeyCodeSingleLower[] = {
112// 0 1 2 3 4 5 6 7 8 9 A B C D E F
113 -1, -1, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', // 0
114 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\n', -1, 'a', 's', // 1
115 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`', -1,'\\', 'z', 'x', 'c', 'v', // 2
116 'b', 'n', 'm', ',', '.', '/', -1, '*', -1, ' ', -1, -1, -1, -1, -1, -1, // 3
117 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
118 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
120 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
121};
122
123static const int KeyCodeMultiLower[] = {
124// 0 1 2 3 4 5 6 7 8 9 A B C D E F
125 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
126 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1
127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2
128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 3
129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
130 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
131 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
132 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
133};
134
135static const int KeyCodeSingleUpper[] = {
136// 0 1 2 3 4 5 6 7 8 9 A B C D E F
137 -1, -1, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', // 0
138 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\n', -1, 'A', 'S', // 1
139 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', -1, '|', 'Z', 'X', 'C', 'V', // 2
140 'B', 'N', 'M', '<', '>', '?', -1, '*', -1, ' ', -1, -1, -1, -1, -1, -1, // 3
141 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
142 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
143 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
144 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
145};
146
147static const int KeyCodeMultiUpper[] = {
148// 0 1 2 3 4 5 6 7 8 9 A B C D E F
149 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
150 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1
151 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2
152 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 3
153 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
154 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
155 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
156 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
157};
158
159/*
160 * state key flags
161 */
162static bool key_lshift;
163static bool key_rshift;
164
165/*
166 * key buffer
167 */
168#define KEY_BUFFER_LEN 10
169
170static uint8_t key_buffer[KEY_BUFFER_LEN];
171static int key_buffer_in_ptr, key_buffer_out_ptr;
172
173static int inq(uint8_t data) {
174 int temp;
175
176 temp = key_buffer_in_ptr + 1;
177 if (temp >= KEY_BUFFER_LEN) {
178 temp = 0;
179 }
180
181 // if in_ptr reaches out_ptr, the queue is full
182 if (temp == key_buffer_out_ptr) {
183 return -1;
184 }
185
186 key_buffer[key_buffer_in_ptr] = data;
187 key_buffer_in_ptr = temp;
188
189 return 0;
190}
191
192static int deq(void) {
193 int rv;
194
195 // if out_ptr reaches in_ptr, the queue is empty
196 if (key_buffer_out_ptr == key_buffer_in_ptr) {
197 return -1;
198 }
199
200 rv = key_buffer[key_buffer_out_ptr];
201 key_buffer_out_ptr++;
202
203 if (key_buffer_out_ptr >= KEY_BUFFER_LEN) {
204 key_buffer_out_ptr = 0;
205 }
206
207 return rv;
208}
209
210static void i8042_process_scode(uint8_t scode, unsigned int flags)
211{
212 static int lastCode = 0;
213 int keyCode;
214 uint8_t keyUpBit;
215
216 bool multi = lastCode == 0xe0;
217
218 // save the key up event bit
219 keyUpBit = scode & 0x80;
220 scode &= 0x7f;
221
222 if (scode == SCANCODE_LSHIFT) {
223 key_lshift = !keyUpBit;
224 }
225
226 if (scode == SCANCODE_RSHIFT) {
227 key_rshift = !keyUpBit;
228 }
229
230 if (key_lshift || key_rshift) {
231 keyCode = multi ? KeyCodeMultiUpper[scode] : KeyCodeSingleUpper[scode];
232 } else {
233 keyCode = multi ? KeyCodeMultiLower[scode] : KeyCodeSingleLower[scode];
234 }
235
236 /*printf_xy(71, 3, BLUE, "%02x%02x %c %c%c", multi ? lastCode : 0, scode,
237 keyCode != -1 ? (char) keyCode : ' ', key_lshift ? 'L' : ' ',
238 key_rshift ? 'R' : ' ');*/
239
240 if (keyCode != -1 && !keyUpBit) {
241 inq(keyCode);
242 }
243
244 // update the last received code
245 lastCode = scode;
246}
247
248static int i8042_wait_read(void)
249{
250 int i = 0;
251 while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
252 delay(1);
253 i++;
254 }
255 return -(i == I8042_CTL_TIMEOUT);
256}
257
258static int i8042_wait_write(void)
259{
260 int i = 0;
261 while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
262 delay(1);
263 i++;
264 }
265 return -(i == I8042_CTL_TIMEOUT);
266}
267
268static int i8042_flush(void)
269{
270 unsigned char data;
271 int i = 0;
272
273 //enter_critical_section();
274
275 while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) {
276 delay(1);
277 data = i8042_read_data();
278 }
279
280 //exit_critical_section();
281
282 return i;
283}
284
285static int i8042_command(uint8_t *param, int command)
286{
287 int retval = 0, i = 0;
288
289 //enter_critical_section();
290
291 retval = i8042_wait_write();
292 if (!retval) {
293 i8042_write_command(command & 0xff);
294 }
295
296 if (!retval) {
297 for (i = 0; i < ((command >> 12) & 0xf); i++) {
298 if ((retval = i8042_wait_write())) {
299 break;
300 }
301
302 i8042_write_data(param[i]);
303 }
304 }
305
306 if (!retval) {
307 for (i = 0; i < ((command & 0xf0) >> 8); i++) {
308 if ((retval = i8042_wait_read())) {
309 break;
310 }
311
312 if (i8042_read_status() & I8042_STR_AUXDATA) {
313 param[i] = ~i8042_read_data();
314 } else {
315 param[i] = i8042_read_data();
316 }
317 }
318 }
319
320 //exit_critical_section();
321
322 return retval;
323}
324
325static enum handler_return i8042_interrupt(void *arg)
326{
327 uint8_t str, data = 0;
328
329 //enter_critical_section();
330 str = i8042_read_status();
331 if (str & I8042_STR_OBF) {
332 data = i8042_read_data();
333 }
334 //exit_critical_section();
335
336 if (str & I8042_STR_OBF) {
337 i8042_process_scode(data,
338 ((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) |
339 ((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0));
340 }
341
342 return INT_NO_RESCHEDULE;
343}
344
345int platform_read_key(char *c)
346{
347 int data;
348
349 enter_critical_section();
350 data = deq();
351
352 if (data != -1) {
353 *c = (char) data;
354 }
355 exit_critical_section();
356
357 return data == -1 ? -1 : 0;
358}
359
360void platform_init_keyboard(void)
361{
362 uint8_t ctr;
363
364 // clear in case of reinit
365 key_buffer_in_ptr = key_buffer_out_ptr = 0;
366
367 i8042_flush();
368
369 if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
370 dprintf(DEBUG, "Failed to read CTR while initializing i8042\n");
371 return;
372 }
373
374 // turn on translation
375 ctr |= I8042_CTR_XLATE;
376
377 // enable keyboard and keyboard irq
378 ctr &= ~I8042_CTR_KBDDIS;
379 ctr |= I8042_CTR_KBDINT;
380
381 if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
382 dprintf(DEBUG, "Failed to write CTR while initializing i8042\n");
383 return;
384 }
385
386 register_int_handler(INT_KEYBOARD, &i8042_interrupt, NULL);
387 unmask_interrupt(INT_KEYBOARD);
388
389 i8042_interrupt(NULL);
390}