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