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