Make TAB+ debug combos work again. Based on bugfix by Andrew Munger.
[reactos.git] / reactos / drivers / input / i8042prt / keyboard.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/input/i8042prt/keyboard.c
5 * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
6 * keyboard specifics
7 * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
8 * Jason Filby (jasonfilby@yahoo.com)
9 * Tinus
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #ifndef NDEBUG
15 #define NDEBUG
16 #endif
17 #include <debug.h>
18
19 #include "i8042prt.h"
20
21 /* GLOBALS *******************************************************************/
22
23 static UCHAR TypematicTable[] = {
24 0x00, 0x00, 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x10, 0x12, /* 0-9 */
25 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1A, /* 10-19 */
26 0x1B, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E };
27
28 typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION {
29 USHORT NumberOfIndicatorKeys;
30 INDICATOR_LIST IndicatorList[3];
31 } LOCAL_KEYBOARD_INDICATOR_TRANSLATION, *PLOCAL_KEYBOARD_INDICATOR_TRANSLATION;
32
33 static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, {
34 {0x3A, KEYBOARD_CAPS_LOCK_ON},
35 {0x45, KEYBOARD_NUM_LOCK_ON},
36 {0x46, KEYBOARD_SCROLL_LOCK_ON}}};
37
38 static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject,
39 PVOID Context);
40
41 /* FUNCTIONS *****************************************************************/
42
43 /*
44 * These functions are callbacks for filter driver custom interrupt
45 * service routines.
46 */
47 VOID STDCALL I8042IsrWritePortKbd(PVOID Context,
48 UCHAR Value)
49 {
50 I8042IsrWritePort(Context, Value, 0);
51 }
52
53 static VOID STDCALL I8042QueueKeyboardPacket(PVOID Context)
54 {
55 PDEVICE_OBJECT DeviceObject = Context;
56 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
57 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
58
59 DevExt->KeyComplete = TRUE;
60 DevExt->KeysInBuffer++;
61 if (DevExt->KeysInBuffer >
62 DevExt->KeyboardAttributes.InputDataQueueLength) {
63 DPRINT1("Keyboard buffer overflow\n");
64 DevExt->KeysInBuffer--;
65 }
66
67 DPRINT("Irq completes key\n");
68 KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
69 }
70
71 /*
72 * These functions are callbacks for filter driver custom
73 * initialization routines.
74 */
75 NTSTATUS STDCALL I8042SynchWritePortKbd(PVOID Context,
76 UCHAR Value,
77 BOOLEAN WaitForAck)
78 {
79 return I8042SynchWritePort((PDEVICE_EXTENSION)Context,
80 0,
81 Value,
82 WaitForAck);
83 }
84
85 BOOLEAN STDCALL I8042InterruptServiceKbd(struct _KINTERRUPT *Interrupt,
86 VOID * Context)
87 {
88 UCHAR Output;
89 UCHAR PortStatus;
90 NTSTATUS Status;
91 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) Context;
92 BOOLEAN HookContinue = FALSE, HookReturn;
93 ULONG Iterations = 0;
94
95 KEYBOARD_INPUT_DATA *InputData =
96 DevExt->KeyboardBuffer + DevExt->KeysInBuffer;
97
98 do {
99 Status = I8042ReadStatus(&PortStatus);
100 DPRINT("PortStatus: %x\n", PortStatus);
101 Status = I8042ReadData(&Output);
102 Iterations++;
103 if (STATUS_SUCCESS == Status)
104 break;
105 KeStallExecutionProcessor(1);
106 } while (Iterations < DevExt->Settings.PollStatusIterations);
107
108 if (STATUS_SUCCESS != Status) {
109 DPRINT("Spurious I8042 interrupt\n");
110 return FALSE;
111 }
112
113 DPRINT("Got: %x\n", Output);
114
115 if (DevExt->KeyboardHook.IsrRoutine) {
116 HookReturn = DevExt->KeyboardHook.IsrRoutine(
117 DevExt->KeyboardHook.Context,
118 InputData,
119 &DevExt->Packet,
120 PortStatus,
121 &Output,
122 &HookContinue,
123 &DevExt->KeyboardScanState);
124
125 if (!HookContinue)
126 return HookReturn;
127 }
128
129 if (I8042PacketIsr(DevExt, Output)) {
130 if (DevExt->PacketComplete) {
131 DPRINT("Packet complete\n");
132 KeInsertQueueDpc(&DevExt->DpcKbd, DevExt, NULL);
133 }
134 DPRINT("Irq eaten by packet\n");
135 return TRUE;
136 }
137
138 DPRINT("Irq is keyboard input\n");
139
140 if (Normal == DevExt->KeyboardScanState) {
141 switch (Output) {
142 case 0xe0:
143 DevExt->KeyboardScanState = GotE0;
144 return TRUE;
145 case 0xe1:
146 DevExt->KeyboardScanState = GotE1;
147 return TRUE;
148 default:
149 ;/* continue */
150 }
151 }
152
153 InputData->Flags = 0;
154
155 switch (DevExt->KeyboardScanState) {
156 case GotE0:
157 InputData->Flags |= KEY_E0;
158 break;
159 case GotE1:
160 InputData->Flags |= KEY_E1;
161 break;
162 default:
163 ;
164 }
165 DevExt->KeyboardScanState = Normal;
166
167 if (Output & 0x80)
168 InputData->Flags |= KEY_BREAK;
169 else
170 InputData->Flags |= KEY_MAKE;
171
172 InputData->MakeCode = Output & 0x7f;
173
174 I8042QueueKeyboardPacket(DevExt->KeyboardObject);
175
176 return TRUE;
177 }
178
179 VOID STDCALL I8042DpcRoutineKbd(PKDPC Dpc,
180 PVOID DeferredContext,
181 PVOID SystemArgument1,
182 PVOID SystemArgument2)
183 {
184 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)SystemArgument1;
185 ULONG KeysTransferred = 0;
186 ULONG KeysInBufferCopy;
187 KIRQL Irql;
188
189 I8042PacketDpc(DevExt);
190
191 if (!DevExt->KeyComplete)
192 return;
193
194 /* We got the interrupt as it was being enabled, too bad */
195 if (!DevExt->HighestDIRQLInterrupt)
196 return;
197
198 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
199
200 DevExt->KeyComplete = FALSE;
201 KeysInBufferCopy = DevExt->KeysInBuffer;
202
203 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
204
205 /* Test for TAB (debugging) */
206 if (DevExt->Settings.CrashSysRq) {
207 PKEYBOARD_INPUT_DATA InputData = DevExt->KeyboardBuffer +
208 KeysInBufferCopy - 1;
209 if (InputData->MakeCode == 0x0F) {
210 DPRINT("Tab!\n");
211 DevExt->TabPressed = !(InputData->Flags & KEY_BREAK);
212 } else if (DevExt->TabPressed) {
213 DPRINT ("Queueing work item %x\n", DevExt->DebugWorkItem);
214 DevExt->DebugKey = InputData->MakeCode;
215 DevExt->TabPressed = FALSE;
216
217 IoQueueWorkItem(DevExt->DebugWorkItem,
218 &(I8042DebugWorkItem),
219 DelayedWorkQueue,
220 DevExt);
221 }
222 }
223
224 DPRINT ("Send a key\n");
225
226 if (!DevExt->KeyboardData.ClassService)
227 return;
228
229 ((PSERVICE_CALLBACK_ROUTINE) DevExt->KeyboardData.ClassService)(
230 DevExt->KeyboardData.ClassDeviceObject,
231 DevExt->KeyboardBuffer,
232 DevExt->KeyboardBuffer + KeysInBufferCopy,
233 &KeysTransferred);
234
235 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
236 DevExt->KeysInBuffer -= KeysTransferred;
237 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
238 }
239
240 /* You have to send the rate/delay in a somewhat awkward format */
241 static UCHAR I8042GetTypematicByte(USHORT Rate, USHORT Delay)
242 {
243 UCHAR ret;
244
245 if (Rate < 3) {
246 ret = 0x0;
247 } else if (Rate > 26) {
248 ret = 0x1F;
249 } else {
250 ret = TypematicTable[Rate];
251 }
252
253 if (Delay < 375) {
254 ;
255 } else if (Delay < 625) {
256 ret |= 0x20;
257 } else if (Delay < 875) {
258 ret |= 0x40;
259 } else {
260 ret |= 0x60;
261 }
262 return ret;
263 }
264 /*
265 * Process the keyboard internal device requests
266 * returns FALSE if it doesn't understand the
267 * call so someone else can handle it.
268 */
269 BOOLEAN STDCALL I8042StartIoKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp)
270 {
271 PIO_STACK_LOCATION Stk;
272 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
273 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
274
275 Stk = IoGetCurrentIrpStackLocation(Irp);
276
277 switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
278 case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER:
279 I8042StartPacket(
280 DevExt,
281 DeviceObject,
282 Stk->Parameters.DeviceIoControl.Type3InputBuffer,
283 Stk->Parameters.DeviceIoControl.InputBufferLength,
284 Irp);
285 break;
286
287 case IOCTL_KEYBOARD_SET_INDICATORS:
288 DevExt->PacketBuffer[0] = 0xED;
289 DevExt->PacketBuffer[1] = 0;
290 if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON)
291 DevExt->PacketBuffer[1] |= 0x04;
292
293 if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON)
294 DevExt->PacketBuffer[1] |= 0x02;
295
296 if (DevExt->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON)
297 DevExt->PacketBuffer[1] |= 0x01;
298
299 I8042StartPacket(DevExt,
300 DeviceObject,
301 DevExt->PacketBuffer,
302 2,
303 Irp);
304 break;
305 case IOCTL_KEYBOARD_SET_TYPEMATIC:
306 DevExt->PacketBuffer[0] = 0xF3;
307 DevExt->PacketBuffer[1] = I8042GetTypematicByte(
308 DevExt->KeyboardTypematic.Rate,
309 DevExt->KeyboardTypematic.Delay);
310
311 I8042StartPacket(DevExt,
312 DeviceObject,
313 DevExt->PacketBuffer,
314 2,
315 Irp);
316 break;
317 default:
318 return FALSE;
319 }
320
321 return TRUE;
322 }
323
324 /*
325 * Runs the keyboard IOCTL_INTERNAL dispatch.
326 * Returns NTSTATUS_INVALID_DEVICE_REQUEST if it doesn't handle this request
327 * so someone else can have a try at it.
328 */
329 NTSTATUS STDCALL I8042InternalDeviceControlKbd(PDEVICE_OBJECT DeviceObject, PIRP Irp)
330 {
331 PIO_STACK_LOCATION Stk;
332 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
333 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
334
335 DPRINT("InternalDeviceControl\n");
336
337 Irp->IoStatus.Information = 0;
338 Stk = IoGetCurrentIrpStackLocation(Irp);
339
340 switch (Stk->Parameters.DeviceIoControl.IoControlCode) {
341
342 case IOCTL_INTERNAL_KEYBOARD_CONNECT:
343 DPRINT("IOCTL_INTERNAL_KEYBOARD_CONNECT\n");
344 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
345 sizeof(CONNECT_DATA)) {
346 DPRINT1("Keyboard IOCTL_INTERNAL_KEYBOARD_CONNECT "
347 "invalid buffer size\n");
348 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
349 goto intcontfailure;
350 }
351
352 if (!DevExt->KeyboardExists) {
353 Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
354 goto intcontfailure;
355 }
356
357 if (DevExt->KeyboardClaimed) {
358 DPRINT1("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
359 "Keyboard is already claimed\n");
360 Irp->IoStatus.Status = STATUS_SHARING_VIOLATION;
361 goto intcontfailure;
362 }
363
364 memcpy(&DevExt->KeyboardData,
365 Stk->Parameters.DeviceIoControl.Type3InputBuffer,
366 sizeof(CONNECT_DATA));
367 DevExt->KeyboardHook.IsrWritePort = I8042IsrWritePortKbd;
368 DevExt->KeyboardHook.QueueKeyboardPacket =
369 I8042QueueKeyboardPacket;
370 DevExt->KeyboardHook.CallContext = DevExt;
371
372 {
373 PIO_WORKITEM WorkItem;
374 PI8042_HOOK_WORKITEM WorkItemData;
375
376 WorkItem = IoAllocateWorkItem(DeviceObject);
377 if (!WorkItem) {
378 DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
379 "Can't allocate work item\n");
380 Irp->IoStatus.Status =
381 STATUS_INSUFFICIENT_RESOURCES;
382 goto intcontfailure;
383 }
384
385 WorkItemData = ExAllocatePoolWithTag(
386 NonPagedPool,
387 sizeof(I8042_HOOK_WORKITEM),
388 TAG_I8042);
389 if (!WorkItemData) {
390 DPRINT ("IOCTL_INTERNAL_KEYBOARD_CONNECT: "
391 "Can't allocate work item data\n");
392 Irp->IoStatus.Status =
393 STATUS_INSUFFICIENT_RESOURCES;
394 IoFreeWorkItem(WorkItem);
395 goto intcontfailure;
396 }
397 WorkItemData->WorkItem = WorkItem;
398 WorkItemData->Target =
399 DevExt->KeyboardData.ClassDeviceObject;
400 WorkItemData->Irp = Irp;
401
402 IoMarkIrpPending(Irp);
403 IoQueueWorkItem(WorkItem,
404 I8042SendHookWorkItem,
405 DelayedWorkQueue,
406 WorkItemData);
407
408 Irp->IoStatus.Status = STATUS_PENDING;
409 }
410
411 break;
412 case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER:
413 DPRINT("IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER\n");
414 if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) {
415 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
416 goto intcontfailure;
417 }
418 if (!DevExt->KeyboardInterruptObject) {
419 Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
420 goto intcontfailure;
421 }
422
423 IoMarkIrpPending(Irp);
424 IoStartPacket(DeviceObject, Irp, NULL, NULL);
425 Irp->IoStatus.Status = STATUS_PENDING;
426
427 break;
428 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
429 DPRINT("IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n");
430 if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
431 sizeof(KEYBOARD_ATTRIBUTES)) {
432 DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_ATTRIBUTES "
433 "invalid buffer size\n");
434 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
435 goto intcontfailure;
436 }
437 memcpy(Irp->AssociatedIrp.SystemBuffer,
438 &DevExt->KeyboardAttributes,
439 sizeof(KEYBOARD_ATTRIBUTES));
440
441 Irp->IoStatus.Status = STATUS_SUCCESS;
442 break;
443 case IOCTL_KEYBOARD_QUERY_INDICATORS:
444 DPRINT("IOCTL_KEYBOARD_QUERY_INDICATORS\n");
445 if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
446 sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
447 DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_INDICATORS "
448 "invalid buffer size\n");
449 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
450 goto intcontfailure;
451 }
452 memcpy(Irp->AssociatedIrp.SystemBuffer,
453 &DevExt->KeyboardIndicators,
454 sizeof(KEYBOARD_INDICATOR_PARAMETERS));
455
456 Irp->IoStatus.Status = STATUS_SUCCESS;
457 break;
458 case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
459 DPRINT("IOCTL_KEYBOARD_QUERY_TYPEMATIC\n");
460 if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
461 sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
462 DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_TYPEMATIC "
463 "invalid buffer size\n");
464 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
465 goto intcontfailure;
466 }
467 memcpy(Irp->AssociatedIrp.SystemBuffer,
468 &DevExt->KeyboardTypematic,
469 sizeof(KEYBOARD_TYPEMATIC_PARAMETERS));
470
471 Irp->IoStatus.Status = STATUS_SUCCESS;
472 break;
473 case IOCTL_KEYBOARD_SET_INDICATORS:
474 DPRINT("IOCTL_KEYBOARD_SET_INDICATORS\n");
475 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
476 sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
477 DPRINT("Keyboard IOCTL_KEYBOARD_SET_INDICTATORS "
478 "invalid buffer size\n");
479 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
480 goto intcontfailure;
481 }
482
483 memcpy(&DevExt->KeyboardIndicators,
484 Irp->AssociatedIrp.SystemBuffer,
485 sizeof(KEYBOARD_INDICATOR_PARAMETERS));
486
487 DPRINT("%x\n", DevExt->KeyboardIndicators.LedFlags);
488
489 IoMarkIrpPending(Irp);
490 IoStartPacket(DeviceObject, Irp, NULL, NULL);
491 Irp->IoStatus.Status = STATUS_PENDING;
492
493 break;
494 case IOCTL_KEYBOARD_SET_TYPEMATIC:
495 DPRINT("IOCTL_KEYBOARD_SET_TYPEMATIC\n");
496 if (Stk->Parameters.DeviceIoControl.InputBufferLength <
497 sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
498 DPRINT("Keyboard IOCTL_KEYBOARD_SET_TYPEMATIC "
499 "invalid buffer size\n");
500 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
501 goto intcontfailure;
502 }
503
504 memcpy(&DevExt->KeyboardTypematic,
505 Irp->AssociatedIrp.SystemBuffer,
506 sizeof(KEYBOARD_TYPEMATIC_PARAMETERS));
507
508 IoMarkIrpPending(Irp);
509 IoStartPacket(DeviceObject, Irp, NULL, NULL);
510 Irp->IoStatus.Status = STATUS_PENDING;
511
512 break;
513 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
514 /* We should check the UnitID, but it's kind of pointless as
515 * all keyboards are supposed to have the same one
516 */
517 if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
518 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) {
519 DPRINT("IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: "
520 "invalid buffer size (expected)\n");
521 /* It's to query the buffer size */
522 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
523 goto intcontfailure;
524 }
525 Irp->IoStatus.Information =
526 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION);
527
528 memcpy(Irp->AssociatedIrp.SystemBuffer,
529 &IndicatorTranslation,
530 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION));
531
532 Irp->IoStatus.Status = STATUS_SUCCESS;
533 break;
534 case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD:
535 /* Nothing to do here */
536 Irp->IoStatus.Status = STATUS_SUCCESS;
537 break;
538 default:
539 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
540 break;
541 }
542
543 intcontfailure:
544 return Irp->IoStatus.Status;
545 }
546
547 /* This is all pretty confusing. There's more than one way to
548 * disable/enable the keyboard. You can send KBD_ENABLE to the
549 * keyboard, and it will start scanning keys. Sending KBD_DISABLE
550 * will disable the key scanning but also reset the parameters to
551 * defaults.
552 *
553 * You can also send 0xAE to the controller for enabling the
554 * keyboard clock line and 0xAD for disabling it. Then it'll
555 * automatically get turned on at the next command. The last
556 * way is by modifying the bit that drives the clock line in the
557 * 'command byte' of the controller. This is almost, but not quite,
558 * the same as the AE/AD thing. The difference can be used to detect
559 * some really old broken keyboard controllers which I hope won't be
560 * necessary.
561 *
562 * Anyway, disabling the keyboard helps the detection and it also
563 * clears the keyboard buffer and sets defaults which is what we
564 * want.
565 */
566
567 BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt)
568 {
569 DPRINT("Enabling keyboard\n");
570 if (STATUS_SUCCESS != I8042SynchWritePort(DevExt,
571 0,
572 KBD_ENABLE,
573 TRUE)) {
574 DPRINT("Can't enable keyboard\n");
575 return FALSE;
576 }
577 return TRUE;
578 }
579
580 BOOLEAN STDCALL I8042KeyboardDefaultsAndDisable(PDEVICE_EXTENSION DevExt)
581 {
582 DPRINT("Disabling keyboard\n");
583 if (STATUS_SUCCESS != I8042SynchWritePort(DevExt,
584 0,
585 KBD_DISABLE,
586 TRUE)) {
587 DPRINT("Can't disable keyboard\n");
588 return FALSE;
589 }
590 return TRUE;
591 }
592
593 BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt)
594 {
595 UCHAR Value;
596 NTSTATUS Status;
597
598 DPRINT("Enabling keyboard interrupt\n");
599
600 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
601 DPRINT1("Can't read i8042 mode\n");
602 return FALSE;
603 }
604
605 Status = I8042ReadDataWait(DevExt, &Value);
606 if (!NT_SUCCESS(Status)) {
607 DPRINT1("No response after read i8042 mode\n");
608 return FALSE;
609 }
610
611 Value &= ~(0x10); // don't disable keyboard
612 Value |= 0x01; // enable keyboard interrupts
613
614 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
615 DPRINT1("Can't set i8042 mode\n");
616 return FALSE;
617 }
618
619 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
620 DPRINT1("Can't send i8042 mode\n");
621 return FALSE;
622 }
623
624 return TRUE;
625 }
626
627 BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt)
628 {
629 NTSTATUS Status;
630 UCHAR Value;
631 ULONG RetryCount = 10;
632
633 DPRINT("Detecting keyboard\n");
634
635 I8042KeyboardDefaultsAndDisable(DevExt);
636
637 do {
638 Status = I8042SynchWritePort(DevExt, 0, KBD_GET_ID, TRUE);
639 } while (STATUS_TIMEOUT == Status && RetryCount--);
640
641 if (!NT_SUCCESS(Status)) {
642 DPRINT1("Can't write GET_ID (%x)\n", Status);
643 return FALSE;
644 }
645
646 Status = I8042ReadDataWait(DevExt, &Value);
647 if (!NT_SUCCESS(Status)) {
648 DPRINT1("No response after GET_ID\n");
649 /* Could be an AT keyboard */
650 DevExt->KeyboardIsAT = TRUE;
651 goto detectsetleds;
652 }
653 DevExt->KeyboardIsAT = FALSE;
654
655 if (Value != 0xAB && Value != 0xAC) {
656 DPRINT("Bad ID: %x\n", Value);
657 /* This is certainly not a keyboard */
658 return FALSE;
659 }
660
661 DPRINT("Keyboard ID: %x", Value);
662
663 Status = I8042ReadDataWait(DevExt, &Value);
664 if (!NT_SUCCESS(Status)) {
665 DPRINT("Partial ID\n");
666 return FALSE;
667 }
668
669 DPRINT ("%x\n", Value);
670
671 detectsetleds:
672 Status = I8042SynchWritePort(DevExt, 0, KBD_SET_LEDS, TRUE);
673 if (!NT_SUCCESS(Status)) {
674 DPRINT("Can't write SET_LEDS (%x)\n", Status);
675 return FALSE;
676 }
677 Status = I8042SynchWritePort(DevExt, 0, 0, TRUE);
678 if (!NT_SUCCESS(Status)) {
679 DPRINT("Can't finish SET_LEDS (%x)\n", Status);
680 return FALSE;
681 }
682
683 // Turn on translation
684
685 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
686 DPRINT1("Can't read i8042 mode\n");
687 return FALSE;
688 }
689
690 Status = I8042ReadDataWait(DevExt, &Value);
691 if (!NT_SUCCESS(Status)) {
692 DPRINT1("No response after read i8042 mode\n");
693 return FALSE;
694 }
695
696 Value |= 0x40; // enable keyboard translation
697
698 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
699 DPRINT1("Can't set i8042 mode\n");
700 return FALSE;
701 }
702
703 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
704 DPRINT1("Can't send i8042 mode\n");
705 return FALSE;
706 }
707
708 return TRUE;
709 }
710
711 /* debug stuff */
712 VOID STDCALL
713 KdpServiceDispatcher(ULONG Code, PVOID Context1, PVOID Context2);
714 #define EnterDebugger ((PVOID)0x25)
715
716 static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject,
717 PVOID Context)
718 {
719 ULONG Key;
720 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
721 PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;
722
723 Key = InterlockedExchange((PLONG)&DevExt->DebugKey, 0);
724 DPRINT("Debug key: %x\n", Key);
725
726 if (!Key)
727 return;
728
729 #ifdef __REACTOS__
730 /* We hope kernel would understand this. If
731 * that's not the case, nothing would happen.
732 */
733 KdpServiceDispatcher(TAG('R', 'o', 's', ' '), (PVOID)Key, NULL);
734 #endif /* __REACTOS__ */
735 }