Merge branch 'ntfs_rebase'
[reactos.git] / drivers / input / i8042prt / keyboard.c
1 /*
2 * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/input/i8042prt/keyboard.c
5 * PURPOSE: Keyboard specific functions
6 * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com)
7 Copyright Jason Filby (jasonfilby@yahoo.com)
8 Copyright Martijn Vernooij (o112w8r02@sneakemail.com)
9 Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org)
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include "i8042prt.h"
15
16 #include <poclass.h>
17 #include <ndk/kdfuncs.h>
18
19 #include <debug.h>
20
21 /* GLOBALS *******************************************************************/
22
23 static IO_WORKITEM_ROUTINE i8042PowerWorkItem;
24 static KDEFERRED_ROUTINE i8042KbdDpcRoutine;
25
26 /* This structure starts with the same layout as KEYBOARD_INDICATOR_TRANSLATION */
27 typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION {
28 USHORT NumberOfIndicatorKeys;
29 INDICATOR_LIST IndicatorList[3];
30 } LOCAL_KEYBOARD_INDICATOR_TRANSLATION, *PLOCAL_KEYBOARD_INDICATOR_TRANSLATION;
31
32 static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, {
33 {0x3A, KEYBOARD_CAPS_LOCK_ON},
34 {0x45, KEYBOARD_NUM_LOCK_ON},
35 {0x46, KEYBOARD_SCROLL_LOCK_ON}}};
36
37 /* FUNCTIONS *****************************************************************/
38
39 /*
40 * These functions are callbacks for filter driver custom interrupt
41 * service routines.
42 */
43 /*static VOID NTAPI
44 i8042KbdIsrWritePort(
45 IN PVOID Context,
46 IN UCHAR Value)
47 {
48 PI8042_KEYBOARD_EXTENSION DeviceExtension;
49
50 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context;
51
52 if (DeviceExtension->KeyboardHook.IsrWritePort)
53 {
54 DeviceExtension->KeyboardHook.IsrWritePort(
55 DeviceExtension->KeyboardHook.CallContext,
56 Value);
57 }
58 else
59 i8042IsrWritePort(Context, Value, 0);
60 }*/
61
62 static VOID NTAPI
63 i8042KbdQueuePacket(
64 IN PVOID Context)
65 {
66 PI8042_KEYBOARD_EXTENSION DeviceExtension;
67
68 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context;
69
70 DeviceExtension->KeyComplete = TRUE;
71 DeviceExtension->KeysInBuffer++;
72 if (DeviceExtension->KeysInBuffer > DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize)
73 {
74 WARN_(I8042PRT, "Keyboard buffer overflow\n");
75 DeviceExtension->KeysInBuffer--;
76 }
77
78 TRACE_(I8042PRT, "Irq completes key\n");
79 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL);
80 }
81
82 /*
83 * These functions are callbacks for filter driver custom
84 * initialization routines.
85 */
86 NTSTATUS NTAPI
87 i8042SynchWritePortKbd(
88 IN PVOID Context,
89 IN UCHAR Value,
90 IN BOOLEAN WaitForAck)
91 {
92 return i8042SynchWritePort(
93 (PPORT_DEVICE_EXTENSION)Context,
94 0,
95 Value,
96 WaitForAck);
97 }
98
99 /*
100 * Process the keyboard internal device requests
101 */
102 VOID NTAPI
103 i8042KbdStartIo(
104 IN PDEVICE_OBJECT DeviceObject,
105 IN PIRP Irp)
106 {
107 PIO_STACK_LOCATION Stack;
108 PI8042_KEYBOARD_EXTENSION DeviceExtension;
109 PPORT_DEVICE_EXTENSION PortDeviceExtension;
110
111 Stack = IoGetCurrentIrpStackLocation(Irp);
112 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension;
113 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
114
115 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
116 {
117 case IOCTL_KEYBOARD_SET_INDICATORS:
118 {
119 TRACE_(I8042PRT, "IOCTL_KEYBOARD_SET_INDICATORS\n");
120 INFO_(I8042PRT, "Leds: {%s%s%s }\n",
121 DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON ? " CAPSLOCK" : "",
122 DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON ? " NUMLOCK" : "",
123 DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON ? " SCROLLLOCK" : "");
124
125 PortDeviceExtension->PacketBuffer[0] = KBD_CMD_SET_LEDS;
126 PortDeviceExtension->PacketBuffer[1] = 0;
127 if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON)
128 PortDeviceExtension->PacketBuffer[1] |= KBD_LED_CAPS;
129
130 if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON)
131 PortDeviceExtension->PacketBuffer[1] |= KBD_LED_NUM;
132
133 if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON)
134 PortDeviceExtension->PacketBuffer[1] |= KBD_LED_SCROLL;
135
136 i8042StartPacket(
137 PortDeviceExtension,
138 &DeviceExtension->Common,
139 PortDeviceExtension->PacketBuffer,
140 2,
141 Irp);
142 break;
143 }
144 default:
145 {
146 ERR_(I8042PRT, "Unknown ioctl code 0x%lx\n",
147 Stack->Parameters.DeviceIoControl.IoControlCode);
148 ASSERT(FALSE);
149 }
150 }
151 }
152
153 static VOID
154 i8042PacketDpc(
155 IN PPORT_DEVICE_EXTENSION DeviceExtension)
156 {
157 BOOLEAN FinishIrp = FALSE;
158 KIRQL Irql;
159 NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */
160
161 /* If the interrupt happens before this is setup, the key
162 * was already in the buffer. Too bad! */
163 if (!DeviceExtension->HighestDIRQLInterrupt)
164 return;
165
166 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt);
167
168 if (DeviceExtension->Packet.State == Idle
169 && DeviceExtension->PacketComplete)
170 {
171 FinishIrp = TRUE;
172 Result = DeviceExtension->PacketResult;
173 DeviceExtension->PacketComplete = FALSE;
174 }
175
176 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql);
177
178 if (!FinishIrp)
179 return;
180
181 if (DeviceExtension->CurrentIrp)
182 {
183 DeviceExtension->CurrentIrp->IoStatus.Status = Result;
184 IoCompleteRequest(DeviceExtension->CurrentIrp, IO_NO_INCREMENT);
185 IoStartNextPacket(DeviceExtension->CurrentIrpDevice, FALSE);
186 DeviceExtension->CurrentIrp = NULL;
187 DeviceExtension->CurrentIrpDevice = NULL;
188 }
189 }
190
191 static VOID NTAPI
192 i8042PowerWorkItem(
193 IN PDEVICE_OBJECT DeviceObject,
194 IN PVOID Context)
195 {
196 PI8042_KEYBOARD_EXTENSION DeviceExtension;
197 PIRP WaitingIrp;
198 NTSTATUS Status;
199
200 UNREFERENCED_PARAMETER(DeviceObject);
201
202 __analysis_assume(Context != NULL);
203 DeviceExtension = Context;
204
205 /* See http://blogs.msdn.com/doronh/archive/2006/09/08/746961.aspx */
206
207 /* Register GUID_DEVICE_SYS_BUTTON interface and report capability */
208 if (DeviceExtension->NewCaps != DeviceExtension->ReportedCaps)
209 {
210 WaitingIrp = InterlockedExchangePointer((PVOID)&DeviceExtension->PowerIrp, NULL);
211 if (WaitingIrp)
212 {
213 /* Cancel the current power irp, as capability changed */
214 WaitingIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
215 WaitingIrp->IoStatus.Information = sizeof(ULONG);
216 IoCompleteRequest(WaitingIrp, IO_NO_INCREMENT);
217 }
218
219 if (DeviceExtension->PowerInterfaceName.MaximumLength == 0)
220 {
221 /* We have never registered this interface ; do it */
222 Status = IoRegisterDeviceInterface(
223 DeviceExtension->Common.Pdo,
224 &GUID_DEVICE_SYS_BUTTON,
225 NULL,
226 &DeviceExtension->PowerInterfaceName);
227 if (!NT_SUCCESS(Status))
228 {
229 /* We can't do more yet, ignore the keypress... */
230 WARN_(I8042PRT, "IoRegisterDeviceInterface(GUID_DEVICE_SYS_BUTTON) failed with status 0x%08lx\n",
231 Status);
232 DeviceExtension->PowerInterfaceName.MaximumLength = 0;
233 return;
234 }
235 }
236 else
237 {
238 /* Disable the interface. Once activated again, capabilities would be asked again */
239 Status = IoSetDeviceInterfaceState(
240 &DeviceExtension->PowerInterfaceName,
241 FALSE);
242 if (!NT_SUCCESS(Status))
243 {
244 /* Ignore the key press... */
245 WARN_(I8042PRT, "Disabling interface %wZ failed with status 0x%08lx\n",
246 &DeviceExtension->PowerInterfaceName, Status);
247 return;
248 }
249 }
250 /* Enable the interface. This leads to receiving a IOCTL_GET_SYS_BUTTON_CAPS,
251 * so we can report new capability */
252 Status = IoSetDeviceInterfaceState(
253 &DeviceExtension->PowerInterfaceName,
254 TRUE);
255 if (!NT_SUCCESS(Status))
256 {
257 /* Ignore the key press... */
258 WARN_(I8042PRT, "Enabling interface %wZ failed with status 0x%08lx\n",
259 &DeviceExtension->PowerInterfaceName, Status);
260 return;
261 }
262 }
263
264 /* Directly complete the IOCTL_GET_SYS_BUTTON_EVENT Irp (if any) */
265 WaitingIrp = InterlockedExchangePointer((PVOID)&DeviceExtension->PowerIrp, NULL);
266 if (WaitingIrp)
267 {
268 PULONG pEvent = (PULONG)WaitingIrp->AssociatedIrp.SystemBuffer;
269
270 WaitingIrp->IoStatus.Status = STATUS_SUCCESS;
271 WaitingIrp->IoStatus.Information = sizeof(ULONG);
272 *pEvent = InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, 0);
273 IoCompleteRequest(WaitingIrp, IO_NO_INCREMENT);
274 }
275 }
276
277 /* Return TRUE if it was a power key */
278 static BOOLEAN
279 HandlePowerKeys(
280 IN PI8042_KEYBOARD_EXTENSION DeviceExtension)
281 {
282 PKEYBOARD_INPUT_DATA InputData;
283 ULONG KeyPress;
284
285 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer - 1;
286 if (!(InputData->Flags & KEY_E0))
287 return FALSE;
288
289 switch (InputData->MakeCode)
290 {
291 case KEYBOARD_POWER_CODE:
292 KeyPress = SYS_BUTTON_POWER;
293 break;
294 case KEYBOARD_SLEEP_CODE:
295 KeyPress = SYS_BUTTON_SLEEP;
296 break;
297 case KEYBOARD_WAKE_CODE:
298 KeyPress = SYS_BUTTON_WAKE;
299 break;
300 default:
301 return FALSE;
302 }
303
304 if (InputData->Flags & KEY_BREAK)
305 /* We already took care of the key press */
306 return TRUE;
307
308 /* Our work can only be done at passive level, so use a workitem */
309 DeviceExtension->NewCaps |= KeyPress;
310 InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, KeyPress);
311 IoQueueWorkItem(
312 DeviceExtension->PowerWorkItem,
313 &i8042PowerWorkItem,
314 DelayedWorkQueue,
315 DeviceExtension);
316 return TRUE;
317 }
318
319 static VOID NTAPI
320 i8042KbdDpcRoutine(
321 IN PKDPC Dpc,
322 IN PVOID DeferredContext,
323 IN PVOID SystemArgument1,
324 IN PVOID SystemArgument2)
325 {
326 PI8042_KEYBOARD_EXTENSION DeviceExtension;
327 PPORT_DEVICE_EXTENSION PortDeviceExtension;
328 ULONG KeysTransferred = 0;
329 ULONG KeysInBufferCopy;
330 KIRQL Irql;
331
332 UNREFERENCED_PARAMETER(Dpc);
333 UNREFERENCED_PARAMETER(SystemArgument1);
334 UNREFERENCED_PARAMETER(SystemArgument2);
335
336 __analysis_assume(DeferredContext != NULL);
337 DeviceExtension = DeferredContext;
338 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
339
340 if (HandlePowerKeys(DeviceExtension))
341 {
342 DeviceExtension->KeyComplete = FALSE;
343 return;
344 }
345
346 i8042PacketDpc(PortDeviceExtension);
347 if (!DeviceExtension->KeyComplete)
348 return;
349 /* We got the interrupt as it was being enabled, too bad */
350 if (!PortDeviceExtension->HighestDIRQLInterrupt)
351 return;
352
353 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
354
355 DeviceExtension->KeyComplete = FALSE;
356 KeysInBufferCopy = DeviceExtension->KeysInBuffer;
357
358 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
359
360 TRACE_(I8042PRT, "Send a key\n");
361
362 if (!DeviceExtension->KeyboardData.ClassService)
363 return;
364
365 INFO_(I8042PRT, "Sending %lu key(s)\n", KeysInBufferCopy);
366 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->KeyboardData.ClassService)(
367 DeviceExtension->KeyboardData.ClassDeviceObject,
368 DeviceExtension->KeyboardBuffer,
369 DeviceExtension->KeyboardBuffer + KeysInBufferCopy,
370 &KeysTransferred);
371
372 /* Validate that the callback didn't change the Irql. */
373 ASSERT(KeGetCurrentIrql() == Irql);
374
375 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt);
376 DeviceExtension->KeysInBuffer -= KeysTransferred;
377 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql);
378 }
379
380 /*
381 * Runs the keyboard IOCTL dispatch.
382 */
383 NTSTATUS NTAPI
384 i8042KbdDeviceControl(
385 IN PDEVICE_OBJECT DeviceObject,
386 IN PIRP Irp)
387 {
388 PIO_STACK_LOCATION Stack;
389 PI8042_KEYBOARD_EXTENSION DeviceExtension;
390 NTSTATUS Status;
391
392 Stack = IoGetCurrentIrpStackLocation(Irp);
393 Irp->IoStatus.Information = 0;
394 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension;
395
396 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
397 {
398 case IOCTL_GET_SYS_BUTTON_CAPS:
399 {
400 /* Part of GUID_DEVICE_SYS_BUTTON interface */
401 PULONG pCaps;
402 TRACE_(I8042PRT, "IOCTL_GET_SYS_BUTTON_CAPS\n");
403
404 if (Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG))
405 Status = STATUS_INVALID_PARAMETER;
406 else
407 {
408 pCaps = (PULONG)Irp->AssociatedIrp.SystemBuffer;
409 *pCaps = DeviceExtension->NewCaps;
410 DeviceExtension->ReportedCaps = DeviceExtension->NewCaps;
411 Irp->IoStatus.Information = sizeof(ULONG);
412 Status = STATUS_SUCCESS;
413 }
414 break;
415 }
416 case IOCTL_GET_SYS_BUTTON_EVENT:
417 {
418 /* Part of GUID_DEVICE_SYS_BUTTON interface */
419 PIRP WaitingIrp;
420 TRACE_(I8042PRT, "IOCTL_GET_SYS_BUTTON_EVENT\n");
421
422 if (Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG))
423 Status = STATUS_INVALID_PARAMETER;
424 else
425 {
426 WaitingIrp = InterlockedCompareExchangePointer(
427 (PVOID)&DeviceExtension->PowerIrp,
428 Irp,
429 NULL);
430 /* Check if an Irp is already pending */
431 if (WaitingIrp)
432 {
433 /* Unable to have a 2nd pending IRP for this IOCTL */
434 WARN_(I8042PRT, "Unable to pend a second IRP for IOCTL_GET_SYS_BUTTON_EVENT\n");
435 Status = STATUS_INVALID_PARAMETER;
436 Irp->IoStatus.Status = Status;
437 IoCompleteRequest(Irp, IO_NO_INCREMENT);
438 }
439 else
440 {
441 ULONG PowerKey;
442 PowerKey = InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, 0);
443 if (PowerKey != 0)
444 {
445 (VOID)InterlockedCompareExchangePointer((PVOID)&DeviceExtension->PowerIrp, NULL, Irp);
446 *(PULONG)Irp->AssociatedIrp.SystemBuffer = PowerKey;
447 Status = STATUS_SUCCESS;
448 Irp->IoStatus.Status = Status;
449 Irp->IoStatus.Information = sizeof(ULONG);
450 IoCompleteRequest(Irp, IO_NO_INCREMENT);
451 }
452 else
453 {
454 TRACE_(I8042PRT, "Pending IOCTL_GET_SYS_BUTTON_EVENT\n");
455 Status = STATUS_PENDING;
456 Irp->IoStatus.Status = Status;
457 IoMarkIrpPending(Irp);
458 }
459 }
460 return Status;
461 }
462 break;
463 }
464 default:
465 {
466 ERR_(I8042PRT, "IRP_MJ_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
467 Stack->Parameters.DeviceIoControl.IoControlCode);
468 ASSERT(FALSE);
469 return ForwardIrpAndForget(DeviceObject, Irp);
470 }
471 }
472
473 if (Status != STATUS_PENDING)
474 {
475 Irp->IoStatus.Status = Status;
476 IoCompleteRequest(Irp, IO_NO_INCREMENT);
477 }
478
479 return Status;
480 }
481
482 VOID
483 NTAPI
484 i8042InitializeKeyboardAttributes(
485 PI8042_KEYBOARD_EXTENSION DeviceExtension)
486 {
487 PPORT_DEVICE_EXTENSION PortDeviceExtension;
488 PI8042_SETTINGS Settings;
489 PKEYBOARD_ATTRIBUTES KeyboardAttributes;
490
491 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
492 Settings = &PortDeviceExtension->Settings;
493
494 KeyboardAttributes = &DeviceExtension->KeyboardAttributes;
495
496 KeyboardAttributes->KeyboardIdentifier.Type = (UCHAR)Settings->OverrideKeyboardType;
497 KeyboardAttributes->KeyboardIdentifier.Subtype = (UCHAR)Settings->OverrideKeyboardSubtype;
498 KeyboardAttributes->NumberOfFunctionKeys = 4;
499 KeyboardAttributes->NumberOfIndicators = 3;
500 KeyboardAttributes->NumberOfKeysTotal = 101;
501 KeyboardAttributes->InputDataQueueLength = Settings->KeyboardDataQueueSize;
502 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0;
503 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate;
504 KeyboardAttributes->KeyRepeatMinimum.Delay = 0;
505 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0;
506 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate;
507 KeyboardAttributes->KeyRepeatMinimum.Delay = 0;
508 }
509
510 /*
511 * Runs the keyboard IOCTL_INTERNAL dispatch.
512 */
513 NTSTATUS NTAPI
514 i8042KbdInternalDeviceControl(
515 IN PDEVICE_OBJECT DeviceObject,
516 IN PIRP Irp)
517 {
518 PIO_STACK_LOCATION Stack;
519 PI8042_KEYBOARD_EXTENSION DeviceExtension;
520 NTSTATUS Status;
521
522 Stack = IoGetCurrentIrpStackLocation(Irp);
523 Irp->IoStatus.Information = 0;
524 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension;
525
526 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
527 {
528 case IOCTL_INTERNAL_KEYBOARD_CONNECT:
529 {
530 SIZE_T Size;
531 PIO_WORKITEM WorkItem = NULL;
532 PI8042_HOOK_WORKITEM WorkItemData = NULL;
533
534 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_CONNECT\n");
535 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA))
536 {
537 Status = STATUS_INVALID_PARAMETER;
538 goto cleanup;
539 }
540
541 DeviceExtension->KeyboardData =
542 *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
543
544 /* Send IOCTL_INTERNAL_I8042_HOOK_KEYBOARD to device stack */
545 WorkItem = IoAllocateWorkItem(DeviceObject);
546 if (!WorkItem)
547 {
548 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
549 Status = STATUS_INSUFFICIENT_RESOURCES;
550 goto cleanup;
551 }
552 WorkItemData = ExAllocatePoolWithTag(
553 NonPagedPool,
554 sizeof(I8042_HOOK_WORKITEM),
555 I8042PRT_TAG);
556 if (!WorkItemData)
557 {
558 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
559 Status = STATUS_NO_MEMORY;
560 goto cleanup;
561 }
562 WorkItemData->WorkItem = WorkItem;
563 WorkItemData->Irp = Irp;
564
565 /* Initialize extension */
566 DeviceExtension->Common.Type = Keyboard;
567 Size = DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize * sizeof(KEYBOARD_INPUT_DATA);
568 DeviceExtension->KeyboardBuffer = ExAllocatePoolWithTag(
569 NonPagedPool,
570 Size,
571 I8042PRT_TAG);
572 if (!DeviceExtension->KeyboardBuffer)
573 {
574 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
575 Status = STATUS_NO_MEMORY;
576 goto cleanup;
577 }
578 RtlZeroMemory(DeviceExtension->KeyboardBuffer, Size);
579 KeInitializeDpc(
580 &DeviceExtension->DpcKeyboard,
581 i8042KbdDpcRoutine,
582 DeviceExtension);
583 DeviceExtension->PowerWorkItem = IoAllocateWorkItem(DeviceObject);
584 if (!DeviceExtension->PowerWorkItem)
585 {
586 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
587 Status = STATUS_INSUFFICIENT_RESOURCES;
588 goto cleanup;
589 }
590 DeviceExtension->DebugWorkItem = IoAllocateWorkItem(DeviceObject);
591 if (!DeviceExtension->DebugWorkItem)
592 {
593 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
594 Status = STATUS_INSUFFICIENT_RESOURCES;
595 goto cleanup;
596 }
597 DeviceExtension->Common.PortDeviceExtension->KeyboardExtension = DeviceExtension;
598 DeviceExtension->Common.PortDeviceExtension->Flags |= KEYBOARD_CONNECTED;
599
600 i8042InitializeKeyboardAttributes(DeviceExtension);
601
602 IoMarkIrpPending(Irp);
603 /* FIXME: DeviceExtension->KeyboardHook.IsrWritePort = ; */
604 DeviceExtension->KeyboardHook.QueueKeyboardPacket = i8042KbdQueuePacket;
605 DeviceExtension->KeyboardHook.CallContext = DeviceExtension;
606 IoQueueWorkItem(WorkItem,
607 i8042SendHookWorkItem,
608 DelayedWorkQueue,
609 WorkItemData);
610 Status = STATUS_PENDING;
611 break;
612
613 cleanup:
614 if (DeviceExtension->KeyboardBuffer)
615 ExFreePoolWithTag(DeviceExtension->KeyboardBuffer, I8042PRT_TAG);
616 if (DeviceExtension->PowerWorkItem)
617 IoFreeWorkItem(DeviceExtension->PowerWorkItem);
618 if (DeviceExtension->DebugWorkItem)
619 IoFreeWorkItem(DeviceExtension->DebugWorkItem);
620 if (WorkItem)
621 IoFreeWorkItem(WorkItem);
622 if (WorkItemData)
623 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG);
624 break;
625 }
626 case IOCTL_INTERNAL_KEYBOARD_DISCONNECT:
627 {
628 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_DISCONNECT\n");
629 /* MSDN says that operation is to implemented.
630 * To implement it, we just have to do:
631 * DeviceExtension->KeyboardData.ClassService = NULL;
632 */
633 Status = STATUS_NOT_IMPLEMENTED;
634 break;
635 }
636 case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD:
637 {
638 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_KEYBOARD\n");
639 /* Nothing to do here */
640 Status = STATUS_SUCCESS;
641 break;
642 }
643 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
644 {
645 PKEYBOARD_ATTRIBUTES KeyboardAttributes;
646
647 /* FIXME: KeyboardAttributes are not initialized anywhere */
648 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n");
649 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_ATTRIBUTES))
650 {
651 Status = STATUS_BUFFER_TOO_SMALL;
652 break;
653 }
654
655 KeyboardAttributes = Irp->AssociatedIrp.SystemBuffer;
656 *KeyboardAttributes = DeviceExtension->KeyboardAttributes;
657
658 Irp->IoStatus.Information = sizeof(KEYBOARD_ATTRIBUTES);
659 Status = STATUS_SUCCESS;
660 break;
661 }
662 case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
663 {
664 DPRINT1("IOCTL_KEYBOARD_QUERY_TYPEMATIC not implemented\n");
665 Status = STATUS_NOT_IMPLEMENTED;
666 break;
667 }
668 case IOCTL_KEYBOARD_SET_TYPEMATIC:
669 {
670 DPRINT1("IOCTL_KEYBOARD_SET_TYPEMATIC not implemented\n");
671 Status = STATUS_NOT_IMPLEMENTED;
672 break;
673 }
674 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
675 {
676 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION\n");
677
678 /* We should check the UnitID, but it's kind of pointless as
679 * all keyboards are supposed to have the same one
680 */
681 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION))
682 {
683 Status = STATUS_BUFFER_TOO_SMALL;
684 }
685 else
686 {
687 RtlCopyMemory(
688 Irp->AssociatedIrp.SystemBuffer,
689 &IndicatorTranslation,
690 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION));
691 Irp->IoStatus.Information = sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION);
692 Status = STATUS_SUCCESS;
693 }
694 break;
695 }
696 case IOCTL_KEYBOARD_QUERY_INDICATORS:
697 {
698 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATORS\n");
699
700 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS))
701 {
702 Status = STATUS_BUFFER_TOO_SMALL;
703 }
704 else
705 {
706 RtlCopyMemory(
707 Irp->AssociatedIrp.SystemBuffer,
708 &DeviceExtension->KeyboardIndicators,
709 sizeof(KEYBOARD_INDICATOR_PARAMETERS));
710 Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
711 Status = STATUS_SUCCESS;
712 }
713 break;
714 }
715 case IOCTL_KEYBOARD_SET_INDICATORS:
716 {
717 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_SET_INDICATORS\n");
718
719 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS))
720 {
721 Status = STATUS_BUFFER_TOO_SMALL;
722 }
723 else
724 {
725 RtlCopyMemory(
726 &DeviceExtension->KeyboardIndicators,
727 Irp->AssociatedIrp.SystemBuffer,
728 sizeof(KEYBOARD_INDICATOR_PARAMETERS));
729 Status = STATUS_PENDING;
730 IoMarkIrpPending(Irp);
731 IoStartPacket(DeviceObject, Irp, NULL, NULL);
732 }
733 break;
734 }
735 default:
736 {
737 ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
738 Stack->Parameters.DeviceIoControl.IoControlCode);
739 ASSERT(FALSE);
740 return ForwardIrpAndForget(DeviceObject, Irp);
741 }
742 }
743
744 if (Status != STATUS_PENDING)
745 {
746 Irp->IoStatus.Status = Status;
747 IoCompleteRequest(Irp, IO_NO_INCREMENT);
748 }
749 return Status;
750 }
751
752 /*
753 * Call the customization hook. The ToReturn parameter is about whether
754 * we should go on with the interrupt. The return value is what
755 * we should return (indicating to the system whether someone else
756 * should try to handle the interrupt)
757 */
758 static BOOLEAN
759 i8042KbdCallIsrHook(
760 IN PI8042_KEYBOARD_EXTENSION DeviceExtension,
761 IN UCHAR Status,
762 IN UCHAR Input,
763 OUT PBOOLEAN ToReturn)
764 {
765 BOOLEAN HookReturn, HookContinue;
766
767 HookContinue = FALSE;
768
769 if (DeviceExtension->KeyboardHook.IsrRoutine)
770 {
771 HookReturn = DeviceExtension->KeyboardHook.IsrRoutine(
772 DeviceExtension->KeyboardHook.Context,
773 DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer,
774 &DeviceExtension->Common.PortDeviceExtension->Packet,
775 Status,
776 &Input,
777 &HookContinue,
778 &DeviceExtension->KeyboardScanState);
779
780 if (!HookContinue)
781 {
782 *ToReturn = HookReturn;
783 return TRUE;
784 }
785 }
786 return FALSE;
787 }
788
789 BOOLEAN NTAPI
790 i8042KbdInterruptService(
791 IN PKINTERRUPT Interrupt,
792 PVOID Context)
793 {
794 PI8042_KEYBOARD_EXTENSION DeviceExtension;
795 PPORT_DEVICE_EXTENSION PortDeviceExtension;
796 PKEYBOARD_INPUT_DATA InputData;
797 ULONG Counter;
798 UCHAR PortStatus = 0, Output = 0;
799 BOOLEAN ToReturn = FALSE;
800 NTSTATUS Status;
801
802 UNREFERENCED_PARAMETER(Interrupt);
803
804 __analysis_assume(Context != NULL);
805 DeviceExtension = Context;
806 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
807 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer;
808 Counter = PortDeviceExtension->Settings.PollStatusIterations;
809
810 while (Counter)
811 {
812 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus);
813 if (!NT_SUCCESS(Status))
814 {
815 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status);
816 return FALSE;
817 }
818 Status = i8042ReadKeyboardData(PortDeviceExtension, &Output);
819 if (NT_SUCCESS(Status))
820 break;
821 KeStallExecutionProcessor(1);
822 Counter--;
823 }
824 if (Counter == 0)
825 {
826 WARN_(I8042PRT, "Spurious i8042 keyboard interrupt\n");
827 return FALSE;
828 }
829
830 INFO_(I8042PRT, "Got: 0x%02x\n", Output);
831
832 if (PortDeviceExtension->Settings.CrashOnCtrlScroll)
833 {
834 /* Test for CTRL + SCROLL LOCK twice */
835 static const UCHAR ScanCodes[] = { 0x1d, 0x46, 0xc6, 0x46, 0 };
836
837 if (Output == ScanCodes[DeviceExtension->ComboPosition])
838 {
839 DeviceExtension->ComboPosition++;
840 if (ScanCodes[DeviceExtension->ComboPosition] == 0)
841 KeBugCheck(MANUALLY_INITIATED_CRASH);
842 }
843 else if (Output == 0xfa)
844 {
845 /* Ignore ACK */
846 }
847 else if (Output == ScanCodes[0])
848 DeviceExtension->ComboPosition = 1;
849 else
850 DeviceExtension->ComboPosition = 0;
851
852 /* Test for TAB + key combination */
853 if (InputData->MakeCode == 0x0F)
854 DeviceExtension->TabPressed = !(InputData->Flags & KEY_BREAK);
855 else if (DeviceExtension->TabPressed)
856 {
857 DeviceExtension->TabPressed = FALSE;
858
859 /* Check which action to do */
860 if (InputData->MakeCode == 0x25)
861 {
862 /* k - Breakpoint */
863 DbgBreakPointWithStatus(DBG_STATUS_SYSRQ);
864 }
865 else if (InputData->MakeCode == 0x30)
866 {
867 /* b - Bugcheck */
868 KeBugCheck(MANUALLY_INITIATED_CRASH);
869 }
870 #if defined(KDBG)
871 else
872 {
873 /* Send request to the kernel debugger.
874 * Unknown requests will be ignored. */
875 KdSystemDebugControl(' soR',
876 (PVOID)(ULONG_PTR)InputData->MakeCode,
877 0,
878 NULL,
879 0,
880 NULL,
881 KernelMode);
882 }
883 #endif
884 }
885 }
886
887 if (i8042KbdCallIsrHook(DeviceExtension, PortStatus, Output, &ToReturn))
888 return ToReturn;
889
890 if (i8042PacketIsr(PortDeviceExtension, Output))
891 {
892 if (PortDeviceExtension->PacketComplete)
893 {
894 TRACE_(I8042PRT, "Packet complete\n");
895 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL);
896 }
897 TRACE_(I8042PRT, "Irq eaten by packet\n");
898 return TRUE;
899 }
900
901 TRACE_(I8042PRT, "Irq is keyboard input\n");
902
903 if (DeviceExtension->KeyboardScanState == Normal)
904 {
905 switch (Output)
906 {
907 case 0xe0:
908 DeviceExtension->KeyboardScanState = GotE0;
909 return TRUE;
910 case 0xe1:
911 DeviceExtension->KeyboardScanState = GotE1;
912 return TRUE;
913 default:
914 break;
915 }
916 }
917
918 /* Update InputData */
919 InputData->Flags = 0;
920 switch (DeviceExtension->KeyboardScanState)
921 {
922 case GotE0:
923 InputData->Flags |= KEY_E0;
924 break;
925 case GotE1:
926 InputData->Flags |= KEY_E1;
927 break;
928 default:
929 break;
930 }
931 DeviceExtension->KeyboardScanState = Normal;
932 if (Output & 0x80)
933 InputData->Flags |= KEY_BREAK;
934 else
935 InputData->Flags |= KEY_MAKE;
936 InputData->MakeCode = Output & 0x7f;
937 InputData->Reserved = 0;
938
939 DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext);
940
941 return TRUE;
942 }