blob: 3478cf2b52c1de29eb193c7fa89c5fb872f65601 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
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/pc.h>
33#include "platform_p.h"
34#include <arch/x86.h>
35#include <lib/cbuf.h>
36
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(lk_time_t delay)
102{
103 lk_time_t start = current_time();
104
105 while (start + delay > current_time());
106}
107
108/* scancodes we want to do something with that don't translate via table */
109#define SCANCODE_LSHIFT 0x2a
110#define SCANCODE_RSHIFT 0x36
111
112/* scancode translation tables */
113static const int KeyCodeSingleLower[] = {
114// 0 1 2 3 4 5 6 7 8 9 A B C D E F
115 -1, -1, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', // 0
116 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\n', -1, 'a', 's', // 1
117 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`', -1,'\\', 'z', 'x', 'c', 'v', // 2
118 'b', 'n', 'm', ',', '.', '/', -1, '*', -1, ' ', -1, -1, -1, -1, -1, -1, // 3
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
120 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
121 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
122 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
123};
124
125static const int KeyCodeMultiLower[] = {
126// 0 1 2 3 4 5 6 7 8 9 A B C D E F
127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1
129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2
130 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 3
131 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
132 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
133 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
134 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
135};
136
137static const int KeyCodeSingleUpper[] = {
138// 0 1 2 3 4 5 6 7 8 9 A B C D E F
139 -1, -1, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', // 0
140 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\n', -1, 'A', 'S', // 1
141 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', -1, '|', 'Z', 'X', 'C', 'V', // 2
142 'B', 'N', 'M', '<', '>', '?', -1, '*', -1, ' ', -1, -1, -1, -1, -1, -1, // 3
143 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
144 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
145 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
146 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
147};
148
149static const int KeyCodeMultiUpper[] = {
150// 0 1 2 3 4 5 6 7 8 9 A B C D E F
151 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
152 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1
153 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2
154 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 3
155 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4
156 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5
157 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6
158 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7
159};
160
161/*
162 * state key flags
163 */
164static bool key_lshift;
165static bool key_rshift;
166
167static cbuf_t key_buf;
168
169static void i8042_process_scode(uint8_t scode, unsigned int flags)
170{
171 static int lastCode = 0;
172 int keyCode;
173 uint8_t keyUpBit;
174
175 bool multi = lastCode == 0xe0;
176
177 // save the key up event bit
178 keyUpBit = scode & 0x80;
179 scode &= 0x7f;
180
181 if (scode == SCANCODE_LSHIFT) {
182 key_lshift = !keyUpBit;
183 }
184
185 if (scode == SCANCODE_RSHIFT) {
186 key_rshift = !keyUpBit;
187 }
188
189 if (key_lshift || key_rshift) {
190 keyCode = multi ? KeyCodeMultiUpper[scode] : KeyCodeSingleUpper[scode];
191 } else {
192 keyCode = multi ? KeyCodeMultiLower[scode] : KeyCodeSingleLower[scode];
193 }
194
195 /*printf_xy(71, 3, BLUE, "%02x%02x %c %c%c", multi ? lastCode : 0, scode,
196 keyCode != -1 ? (char) keyCode : ' ', key_lshift ? 'L' : ' ',
197 key_rshift ? 'R' : ' ');*/
198
199 if (keyCode != -1 && !keyUpBit) {
200 char c = (char) keyCode;
201 cbuf_write_char(&key_buf, c, false);
202 }
203
204 // update the last received code
205 lastCode = scode;
206}
207
208static int i8042_wait_read(void)
209{
210 int i = 0;
211 while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
212 delay(1);
213 i++;
214 }
215 return -(i == I8042_CTL_TIMEOUT);
216}
217
218static int i8042_wait_write(void)
219{
220 int i = 0;
221 while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
222 delay(1);
223 i++;
224 }
225 return -(i == I8042_CTL_TIMEOUT);
226}
227
228static int i8042_flush(void)
229{
230 unsigned char data __UNUSED;
231 int i = 0;
232
233 //enter_critical_section();
234
235 while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) {
236 delay(1);
237 data = i8042_read_data();
238 }
239
240 //exit_critical_section();
241
242 return i;
243}
244
245static int i8042_command(uint8_t *param, int command)
246{
247 int retval = 0, i = 0;
248
249 //enter_critical_section();
250
251 retval = i8042_wait_write();
252 if (!retval) {
253 i8042_write_command(command & 0xff);
254 }
255
256 if (!retval) {
257 for (i = 0; i < ((command >> 12) & 0xf); i++) {
258 if ((retval = i8042_wait_write())) {
259 break;
260 }
261
262 i8042_write_data(param[i]);
263 }
264 }
265
266 if (!retval) {
267 for (i = 0; i < ((command & 0xf0) >> 8); i++) {
268 if ((retval = i8042_wait_read())) {
269 break;
270 }
271
272 if (i8042_read_status() & I8042_STR_AUXDATA) {
273 param[i] = ~i8042_read_data();
274 } else {
275 param[i] = i8042_read_data();
276 }
277 }
278 }
279
280 //exit_critical_section();
281
282 return retval;
283}
284
285static enum handler_return i8042_interrupt(void *arg)
286{
287 uint8_t str, data = 0;
288
289 //enter_critical_section();
290 str = i8042_read_status();
291 if (str & I8042_STR_OBF) {
292 data = i8042_read_data();
293 }
294 //exit_critical_section();
295
296 if (str & I8042_STR_OBF) {
297 i8042_process_scode(data,
298 ((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) |
299 ((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0));
300 }
301
302 return INT_NO_RESCHEDULE;
303}
304
305int platform_read_key(char *c)
306{
307 ssize_t len;
308
309 len = cbuf_read_char(&key_buf, c, true);
310 return len;
311}
312
313void platform_init_keyboard(void)
314{
315 uint8_t ctr;
316
317 cbuf_initialize(&key_buf, 32);
318
319 i8042_flush();
320
321 if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
322 dprintf(SPEW, "Failed to read CTR while initializing i8042\n");
323 return;
324 }
325
326 // turn on translation
327 ctr |= I8042_CTR_XLATE;
328
329 // enable keyboard and keyboard irq
330 ctr &= ~I8042_CTR_KBDDIS;
331 ctr |= I8042_CTR_KBDINT;
332
333 if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
334 dprintf(SPEW, "Failed to write CTR while initializing i8042\n");
335 return;
336 }
337
338 register_int_handler(INT_KEYBOARD, &i8042_interrupt, NULL);
339 unmask_interrupt(INT_KEYBOARD);
340
341 i8042_interrupt(NULL);
342}