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