revert between 56095 -> 55830 in arch
[AROS.git] / rom / hidds / i8042 / kbdclass.c
blob10f0fc16892cd2f4b8655b300850b783aeabf479
1 /*
2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: The main keyboard class.
6 Lang: English.
7 */
9 /****************************************************************************************/
11 #define AROS_ALMOST_COMPATIBLE
13 #include <proto/exec.h>
14 #include <proto/utility.h>
15 #include <proto/oop.h>
16 #include <proto/kernel.h>
17 #include <aros/system.h>
18 #include <aros/symbolsets.h>
19 #include <oop/oop.h>
20 #include <exec/alerts.h>
21 #include <exec/memory.h>
22 #include <hidd/hidd.h>
23 #include <hidd/keyboard.h>
24 #include <hardware/custom.h>
25 #include <devices/inputevent.h>
26 #include <devices/rawkeycodes.h>
28 #include "kbd.h"
29 #include "kbd_common.h"
30 #include "keys.h"
32 #define DEBUG 0
33 #include <aros/debug.h>
35 /****************************************************************************************/
37 /* Predefinitions */
39 void kbd_process_key(struct kbd_data *, UBYTE,
40 struct ExecBase *SysBase);
42 void kbd_updateleds();
43 int kbd_reset(void);
45 /****************************************************************************************/
47 #define NOKEY -1
49 /****************************************************************************************/
51 #include "stdkeytable.h"
53 /****************************************************************************************/
55 #include "e0keytable.h"
57 /****************************************************************************************/
58 static void kbd_keyint(struct kbd_data *data, void *unused)
60 UBYTE keycode; /* Recent Keycode get */
61 UBYTE info = 0; /* Data from info reg */
62 WORD work = 10000;
64 D(bug("ki: {\n"));
65 for(; ((info = kbd_read_status()) & KBD_STATUS_OBF) && work; work--)
67 /* data from information port */
68 if (info & KBD_STATUS_MOUSE_OBF)
71 ** Data from PS/2 mouse. Hopefully this gets through to mouse interrupt
72 ** if we break out of loop here :-\
74 break;
76 keycode = kbd_read_input();
78 D(bug("ki: keycode %d (%x)\n", keycode, keycode));
79 if (info & (KBD_STATUS_GTO | KBD_STATUS_PERR))
81 /* Ignore errors and messages for mouse -> eat status/error byte */
82 continue;
85 kbd_process_key(data, keycode, SysBase);
86 } /* for(; ((info = kbd_read_status()) & KBD_STATUS_OBF) && work; work--) */
88 D(if (!work) bug("kbd.hidd: controller jammed (0x%02X).\n", info);)
89 D(bug("ki: }\n"));
93 OOP_Object * PCKbd__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
95 struct TagItem *tag, *tstate;
96 APTR callback = NULL;
97 APTR callbackdata = NULL;
98 int reset_success;
99 int last_code;
101 EnterFunc(bug("Kbd::New()\n"));
103 #if __WORDSIZE == 32 /* FIXME: REMOVEME: just a debugging thing for the weird s-key problem */
104 SysBase->ex_Reserved2[1] = (ULONG)std_keytable;
105 #endif
106 if (XSD(cl)->kbdhidd) /* Cannot open twice */
107 ReturnPtr("Kbd::New", OOP_Object *, NULL); /* Should have some error code here */
109 tstate = msg->attrList;
110 D(bug("Kbd: tstate: %p, tag=%x\n", tstate, tstate->ti_Tag));
112 while ((tag = NextTagItem(&tstate)))
114 ULONG idx;
116 D(bug("Kbd: Got tag %d, data %x\n", tag->ti_Tag, tag->ti_Data));
118 if (IS_HIDDKBD_ATTR(tag->ti_Tag, idx))
120 D(bug("Kbd hidd tag\n"));
121 switch (idx)
123 case aoHidd_Kbd_IrqHandler:
124 callback = (APTR)tag->ti_Data;
125 D(bug("Got callback %p\n", (APTR)tag->ti_Data));
126 break;
128 case aoHidd_Kbd_IrqHandlerData:
129 callbackdata = (APTR)tag->ti_Data;
130 D(bug("Got data %p\n", (APTR)tag->ti_Data));
131 break;
135 } /* while (tags to process) */
137 if (NULL == callback)
138 ReturnPtr("Kbd::New", OOP_Object *, NULL); /* Should have some error code here */
140 /* Only continue if there appears to be a keyboard controller */
141 Disable();
142 last_code = kbd_clear_input();
143 kbd_write_command_w(KBD_CTRLCMD_SELF_TEST);
144 reset_success = kbd_wait_for_input();
145 Enable();
147 if (reset_success == 0x55)
149 /* Add some descriptional tags to our attributes */
150 struct TagItem kbd_tags[] =
152 {aHidd_Name , (IPTR)"i8042.hidd" },
153 {aHidd_HardwareName, (IPTR)"IBM AT-compatible keyboard"},
154 {TAG_MORE , (IPTR)msg->attrList }
156 struct pRoot_New new_msg =
158 .mID = msg->mID,
159 .attrList = kbd_tags
162 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, &new_msg.mID);
163 if (o)
165 struct kbd_data *data = OOP_INST_DATA(cl, o);
167 data->kbd_callback = (VOID (*)(APTR, UWORD))callback;
168 data->callbackdata = callbackdata;
169 data->prev_amigacode = -2;
170 data->prev_keycode = 0;
172 Disable();
173 kbd_reset(); /* Reset the keyboard */
174 kbd_updateleds(0);
175 Enable();
178 * Report last key received before keyboard was reset, so that
179 * keyboard.device knows about any key currently held down
181 if (last_code > 0)
182 kbd_process_key(data, (UBYTE)last_code, SysBase);
184 /* Install keyboard interrupt */
185 data->irq = KrnAddIRQHandler(1, kbd_keyint, data, NULL);
187 ReturnPtr("Kbd::New", OOP_Object *, o);
188 } /* if (o) */
190 D(else bug("Keyboard controller not detected\n");)
192 return NULL;
195 VOID PCKbd__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
197 struct kbd_data *data = OOP_INST_DATA(cl, o);
199 KrnRemIRQHandler(data->irq);
200 XSD(cl)->kbdhidd = NULL;
202 OOP_DoSuperMethod(cl, o, msg);
205 /****************************************************************************************/
207 #define WaitForInput \
208 ({ int timeout=1000; \
209 do \
211 info = kbd_read_status(); \
212 if (!--timeout) \
213 break; \
214 } while((info & KBD_STATUS_OBF));\
215 inb(0x80); /* Read from port 0x80, the debug port, to add some delay */ \
218 /****************************************************************************************/
220 #define FLAG_LCTRL 0x00000008
221 #define FLAG_RCTRL 0x00000010
222 #define FLAG_LALT 0x00000020
223 #define FLAG_RALT 0x00000040
224 #define FLAG_LSHIFT 0x00000080
225 #define FLAG_RSHIFT 0x00000100
226 #define FLAG_LMETA 0x00000200
227 #define FLAG_RMETA 0x00000400
228 #define FLAG_DEL 0x00000800
230 /****************************************************************************************/
232 void kbd_updateleds(ULONG kbd_keystate)
234 UBYTE info;
235 kbd_write_output_w(KBD_OUTCMD_SET_LEDS);
236 WaitForInput;
237 kbd_read_input();
238 kbd_write_output_w(kbd_keystate & 0x07);
239 WaitForInput;
240 kbd_read_input();
243 #undef SysBase
245 void kbd_process_key(struct kbd_data *data, UBYTE keycode,
246 struct ExecBase *SysBase)
248 ULONG kbd_keystate = data->kbd_keystate;
249 UBYTE downkeycode;
250 UBYTE releaseflag;
251 UWORD event;
252 WORD amigacode;
254 if ((keycode == KBD_REPLY_ACK) || (keycode == KBD_REPLY_RESEND))
256 /* Ignore these */
257 return;
260 if ((keycode == 0xE0) || (keycode == 0xE1))
262 /* Extended keycodes: E0 gets followed by one code, E1 by two */
263 data->prev_keycode = keycode;
264 return;
267 if ((keycode == 0x00) || (keycode == 0xFF))
269 /* 00 is error. FF is sent by some keyboards -> ignore it. */
270 data->prev_keycode = 0;
271 return;
274 amigacode = NOKEY;
275 event = 0;
276 downkeycode = keycode & 0x7F;
277 releaseflag = keycode & 0x80;
279 if (data->prev_keycode)
281 if (data->prev_keycode == 0xE0)
283 data->prev_keycode = 0;
284 event = 0x4000 | keycode;
286 if (downkeycode < NUM_E0KEYS)
288 amigacode = e0_keytable[downkeycode];
289 if (amigacode != NOKEY) amigacode |= releaseflag;
291 } /* if (data->prev_keycode == 0xE0) */
292 else
294 /* Check Pause key: 0xE1 0x1D 0x45 0xE1 0x9D 0xC5 */
295 if ((data->prev_keycode == 0xE1) && (downkeycode == 0x1D))
297 /* let's remember, that we still need third key */
298 data->prev_keycode = 0x1234;
299 return;
301 else if ((data->prev_keycode == 0x1234) && (downkeycode == 0x45))
303 /* Got third key and yes, it is Pause */
304 amigacode = 0x6E | releaseflag;
305 data->prev_keycode = 0;
307 else
309 /* Unknown */
310 data->prev_keycode = 0;
311 return;
314 } /* if (data->prev_keycode == 0xE0) else ... */
316 } /* if (data->prev_keycode) */
317 else
319 /* Normal single byte keycode */
320 event = keycode;
321 if (downkeycode < NUM_STDKEYS)
323 amigacode = std_keytable[downkeycode];
324 if (amigacode != NOKEY) amigacode |= releaseflag;
328 switch(event)
330 case K_KP_Numl:
331 kbd_keystate ^= 0x02; /* Toggle Numlock bit */
332 kbd_updateleds(kbd_keystate);
333 break;
335 case K_Scroll_Lock:
336 kbd_keystate ^= 0x01; /* Toggle Scrolllock bit */
337 kbd_updateleds(kbd_keystate);
338 break;
340 case K_CapsLock:
341 kbd_keystate ^= 0x04; /* Toggle Capslock bit */
342 kbd_updateleds(kbd_keystate);
343 break;
345 case K_LShift:
346 kbd_keystate |= FLAG_LSHIFT;
347 break;
349 case (K_LShift | 0x80):
350 kbd_keystate &= ~FLAG_LSHIFT;
351 break;
353 case K_RShift:
354 kbd_keystate |= FLAG_RSHIFT;
355 break;
357 case (K_RShift | 0x80):
358 kbd_keystate &= ~FLAG_RSHIFT;
359 break;
361 case K_LCtrl:
362 kbd_keystate |= FLAG_LCTRL;
363 break;
365 case (K_LCtrl | 0x80):
366 kbd_keystate &= ~FLAG_LCTRL;
367 break;
369 case K_RCtrl:
370 kbd_keystate |= FLAG_RCTRL;
371 break;
373 case (K_RCtrl | 0x80):
374 kbd_keystate &= ~FLAG_RCTRL;
375 break;
377 case K_LMeta:
378 kbd_keystate |= FLAG_LMETA;
379 break;
381 case (K_LMeta | 0x80):
382 kbd_keystate &= ~FLAG_LMETA;
383 break;
385 case K_RMeta:
386 case K_Menu:
387 kbd_keystate |= FLAG_RMETA;
388 break;
390 case (K_RMeta | 0x80):
391 case (K_Menu | 0x80):
392 kbd_keystate &= ~FLAG_RMETA;
393 break;
395 case K_LAlt:
396 kbd_keystate |= FLAG_LALT;
397 break;
399 case (K_LAlt | 0x80):
400 kbd_keystate &= ~FLAG_LALT;
401 break;
403 case K_RAlt:
404 kbd_keystate |= FLAG_RALT;
405 break;
407 case (K_RAlt | 0x80):
408 kbd_keystate &= ~FLAG_RALT;
409 break;
411 case K_Del:
412 kbd_keystate |= FLAG_DEL;
413 break;
415 case (K_Del | 0x80):
416 kbd_keystate &= ~FLAG_DEL;
417 break;
419 } /* switch(event) */
421 if ((kbd_keystate & (FLAG_LCTRL | FLAG_RCTRL)) != 0
422 && (kbd_keystate & (FLAG_LALT | FLAG_RALT)) != 0
423 && (kbd_keystate & FLAG_DEL) != 0)
425 ShutdownA(SD_ACTION_COLDREBOOT);
428 if ((kbd_keystate & (FLAG_LCTRL | FLAG_LMETA | FLAG_RMETA))
429 == (FLAG_LCTRL | FLAG_LMETA | FLAG_RMETA))
431 amigacode = 0x78; /* Reset */
434 D(bug("ki: amigacode %d (%x) last %d (%x)\n", amigacode, amigacode,
435 data->prev_amigacode, data->prev_amigacode));
437 /* Update keystate */
438 data->kbd_keystate = kbd_keystate;
440 #if 0
441 if (amigacode == 0x78) // Reset request
442 ColdReboot();
443 #endif
445 if (amigacode == NOKEY) return;
447 if (amigacode == data->prev_amigacode)
450 ** Must be a repeated key. Ignore it, because we have our
451 ** own kbd repeating in input.device
453 return;
456 data->prev_amigacode = amigacode;
458 D(bug("ki: ********************* c %d (%x)\n", amigacode, amigacode));
460 /* Pass the code to handler */
461 data->kbd_callback(data->callbackdata, amigacode);
463 return;
466 /****************************************************************************************/
468 /* FIXME: This should go somewhere higher but D(bug()) is not possible there */
469 #undef D
470 #undef BUG
471 #define D(x)
472 #define BUG
474 /****************************************************************************************/
477 * Please leave this routine as is for now.
478 * It works and that is all that matters right now.
481 /****************************************************************************************/
483 int kbd_reset(void)
485 UBYTE status;
487 kbd_write_command_w(KBD_CTRLCMD_SELF_TEST); /* Initialize and test keyboard controller */
489 if (kbd_wait_for_input() != 0x55)
491 D(bug("Kbd: Controller test failed!\n"));
492 return FALSE;
495 kbd_write_command_w(KBD_CTRLCMD_KBD_TEST);
497 if (kbd_wait_for_input() != 0)
499 D(bug("Kbd: Keyboard test failed!\n"));
500 return FALSE;
503 kbd_write_command_w(KBD_CTRLCMD_KBD_ENABLE); /* enable keyboard */
505 D(bug("Kbd: Keyboard enabled!\n"));
509 kbd_write_output_w(KBD_OUTCMD_RESET);
510 status = kbd_wait_for_input();
512 if (status == KBD_REPLY_ACK)
513 break;
515 if (status != KBD_REPLY_RESEND)
517 D(bug("Kbd: Keyboard reset failed! (1)\n"));
518 return FALSE;
520 } while(1);
522 if (kbd_wait_for_input() != KBD_REPLY_POR)
524 D(bug("Kbd: Keyboard reset failed! (2)\n"));
525 return FALSE;
530 kbd_write_output_w(KBD_OUTCMD_DISABLE);
531 status = kbd_wait_for_input();
533 if (status == KBD_REPLY_ACK)
534 break;
536 if (status != KBD_REPLY_RESEND)
538 D(bug("Kbd: Keyboard disable failed!\n"));
539 return FALSE;
541 } while (1);
543 kbd_write_command_w(KBD_CTRLCMD_WRITE_MODE); /* Write mode */
545 #if 0
546 kbd_write_output_w( KBD_MODE_KCC | // set parameters: scan code to pc conversion,
547 KBD_MODE_KBD_INT | // enable mouse and keyboard,
548 KBD_MODE_DISABLE_MOUSE | // enable IRQ 1 & 12.
549 KBD_MODE_SYS);
550 #else
551 kbd_write_output_w( KBD_MODE_KCC | KBD_MODE_KBD_INT);
552 #endif
554 kbd_write_output_w(KBD_OUTCMD_ENABLE);
556 D(bug("Kbd: enabled ints\n"));
558 if (kbd_wait_for_input() != KBD_REPLY_ACK)
560 D(bug("Kbd: No REPLY_ACK !!!\nReturning FALSE !!!!\n"));
561 return FALSE;
564 D(bug("Kbd: Successfully reset keyboard!\n"));
566 return TRUE;
569 /****************************************************************************************/