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
i8042KbdQueuePacket(IN PVOID Context)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
i8042SynchWritePortKbd(IN PVOID Context,IN UCHAR Value,IN BOOLEAN WaitForAck)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
i8042KbdStartIo(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
i8042PacketDpc(IN PPORT_DEVICE_EXTENSION DeviceExtension)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
i8042PowerWorkItem(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)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
HandlePowerKeys(IN PI8042_KEYBOARD_EXTENSION DeviceExtension)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
i8042KbdDpcRoutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)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
i8042KbdDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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 return ForwardIrpAndForget(DeviceObject, Irp);
469 }
470 }
471
472 if (Status != STATUS_PENDING)
473 {
474 Irp->IoStatus.Status = Status;
475 IoCompleteRequest(Irp, IO_NO_INCREMENT);
476 }
477
478 return Status;
479 }
480
481 VOID
482 NTAPI
i8042InitializeKeyboardAttributes(PI8042_KEYBOARD_EXTENSION DeviceExtension)483 i8042InitializeKeyboardAttributes(
484 PI8042_KEYBOARD_EXTENSION DeviceExtension)
485 {
486 PPORT_DEVICE_EXTENSION PortDeviceExtension;
487 PI8042_SETTINGS Settings;
488 PKEYBOARD_ATTRIBUTES KeyboardAttributes;
489
490 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
491 Settings = &PortDeviceExtension->Settings;
492
493 KeyboardAttributes = &DeviceExtension->KeyboardAttributes;
494
495 KeyboardAttributes->KeyboardIdentifier.Type = (UCHAR)Settings->OverrideKeyboardType;
496 KeyboardAttributes->KeyboardIdentifier.Subtype = (UCHAR)Settings->OverrideKeyboardSubtype;
497 KeyboardAttributes->NumberOfFunctionKeys = 4;
498 KeyboardAttributes->NumberOfIndicators = 3;
499 KeyboardAttributes->NumberOfKeysTotal = 101;
500 KeyboardAttributes->InputDataQueueLength = Settings->KeyboardDataQueueSize;
501 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0;
502 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate;
503 KeyboardAttributes->KeyRepeatMinimum.Delay = 0;
504 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0;
505 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate;
506 KeyboardAttributes->KeyRepeatMinimum.Delay = 0;
507 }
508
509 /*
510 * Runs the keyboard IOCTL_INTERNAL dispatch.
511 */
512 NTSTATUS NTAPI
i8042KbdInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)513 i8042KbdInternalDeviceControl(
514 IN PDEVICE_OBJECT DeviceObject,
515 IN PIRP Irp)
516 {
517 PIO_STACK_LOCATION Stack;
518 PI8042_KEYBOARD_EXTENSION DeviceExtension;
519 NTSTATUS Status;
520
521 Stack = IoGetCurrentIrpStackLocation(Irp);
522 Irp->IoStatus.Information = 0;
523 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension;
524
525 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
526 {
527 case IOCTL_INTERNAL_KEYBOARD_CONNECT:
528 {
529 SIZE_T Size;
530 PIO_WORKITEM WorkItem = NULL;
531 PI8042_HOOK_WORKITEM WorkItemData = NULL;
532
533 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_CONNECT\n");
534 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA))
535 {
536 Status = STATUS_INVALID_PARAMETER;
537 goto cleanup;
538 }
539
540 DeviceExtension->KeyboardData =
541 *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
542
543 /* Send IOCTL_INTERNAL_I8042_HOOK_KEYBOARD to device stack */
544 WorkItem = IoAllocateWorkItem(DeviceObject);
545 if (!WorkItem)
546 {
547 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
548 Status = STATUS_INSUFFICIENT_RESOURCES;
549 goto cleanup;
550 }
551 WorkItemData = ExAllocatePoolWithTag(
552 NonPagedPool,
553 sizeof(I8042_HOOK_WORKITEM),
554 I8042PRT_TAG);
555 if (!WorkItemData)
556 {
557 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
558 Status = STATUS_NO_MEMORY;
559 goto cleanup;
560 }
561 WorkItemData->WorkItem = WorkItem;
562 WorkItemData->Irp = Irp;
563
564 /* Initialize extension */
565 DeviceExtension->Common.Type = Keyboard;
566 Size = DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize * sizeof(KEYBOARD_INPUT_DATA);
567 DeviceExtension->KeyboardBuffer = ExAllocatePoolWithTag(
568 NonPagedPool,
569 Size,
570 I8042PRT_TAG);
571 if (!DeviceExtension->KeyboardBuffer)
572 {
573 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n");
574 Status = STATUS_NO_MEMORY;
575 goto cleanup;
576 }
577 RtlZeroMemory(DeviceExtension->KeyboardBuffer, Size);
578 KeInitializeDpc(
579 &DeviceExtension->DpcKeyboard,
580 i8042KbdDpcRoutine,
581 DeviceExtension);
582 DeviceExtension->PowerWorkItem = IoAllocateWorkItem(DeviceObject);
583 if (!DeviceExtension->PowerWorkItem)
584 {
585 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
586 Status = STATUS_INSUFFICIENT_RESOURCES;
587 goto cleanup;
588 }
589 DeviceExtension->DebugWorkItem = IoAllocateWorkItem(DeviceObject);
590 if (!DeviceExtension->DebugWorkItem)
591 {
592 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n");
593 Status = STATUS_INSUFFICIENT_RESOURCES;
594 goto cleanup;
595 }
596 DeviceExtension->Common.PortDeviceExtension->KeyboardExtension = DeviceExtension;
597 DeviceExtension->Common.PortDeviceExtension->Flags |= KEYBOARD_CONNECTED;
598
599 i8042InitializeKeyboardAttributes(DeviceExtension);
600
601 IoMarkIrpPending(Irp);
602 /* FIXME: DeviceExtension->KeyboardHook.IsrWritePort = ; */
603 DeviceExtension->KeyboardHook.QueueKeyboardPacket = i8042KbdQueuePacket;
604 DeviceExtension->KeyboardHook.CallContext = DeviceExtension;
605 IoQueueWorkItem(WorkItem,
606 i8042SendHookWorkItem,
607 DelayedWorkQueue,
608 WorkItemData);
609 Status = STATUS_PENDING;
610 break;
611
612 cleanup:
613 if (DeviceExtension->KeyboardBuffer)
614 ExFreePoolWithTag(DeviceExtension->KeyboardBuffer, I8042PRT_TAG);
615 if (DeviceExtension->PowerWorkItem)
616 IoFreeWorkItem(DeviceExtension->PowerWorkItem);
617 if (DeviceExtension->DebugWorkItem)
618 IoFreeWorkItem(DeviceExtension->DebugWorkItem);
619 if (WorkItem)
620 IoFreeWorkItem(WorkItem);
621 if (WorkItemData)
622 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG);
623 break;
624 }
625 case IOCTL_INTERNAL_KEYBOARD_DISCONNECT:
626 {
627 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_DISCONNECT\n");
628 /* MSDN says that operation is to implemented.
629 * To implement it, we just have to do:
630 * DeviceExtension->KeyboardData.ClassService = NULL;
631 */
632 Status = STATUS_NOT_IMPLEMENTED;
633 break;
634 }
635 case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD:
636 {
637 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_KEYBOARD\n");
638 /* Nothing to do here */
639 Status = STATUS_SUCCESS;
640 break;
641 }
642 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
643 {
644 PKEYBOARD_ATTRIBUTES KeyboardAttributes;
645
646 /* FIXME: KeyboardAttributes are not initialized anywhere */
647 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n");
648 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_ATTRIBUTES))
649 {
650 Status = STATUS_BUFFER_TOO_SMALL;
651 break;
652 }
653
654 KeyboardAttributes = Irp->AssociatedIrp.SystemBuffer;
655 *KeyboardAttributes = DeviceExtension->KeyboardAttributes;
656
657 Irp->IoStatus.Information = sizeof(KEYBOARD_ATTRIBUTES);
658 Status = STATUS_SUCCESS;
659 break;
660 }
661 case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
662 {
663 DPRINT1("IOCTL_KEYBOARD_QUERY_TYPEMATIC not implemented\n");
664 Status = STATUS_NOT_IMPLEMENTED;
665 break;
666 }
667 case IOCTL_KEYBOARD_SET_TYPEMATIC:
668 {
669 DPRINT1("IOCTL_KEYBOARD_SET_TYPEMATIC not implemented\n");
670 Status = STATUS_NOT_IMPLEMENTED;
671 break;
672 }
673 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
674 {
675 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION\n");
676
677 /* We should check the UnitID, but it's kind of pointless as
678 * all keyboards are supposed to have the same one
679 */
680 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION))
681 {
682 Status = STATUS_BUFFER_TOO_SMALL;
683 }
684 else
685 {
686 RtlCopyMemory(
687 Irp->AssociatedIrp.SystemBuffer,
688 &IndicatorTranslation,
689 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION));
690 Irp->IoStatus.Information = sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION);
691 Status = STATUS_SUCCESS;
692 }
693 break;
694 }
695 case IOCTL_KEYBOARD_QUERY_INDICATORS:
696 {
697 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATORS\n");
698
699 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS))
700 {
701 Status = STATUS_BUFFER_TOO_SMALL;
702 }
703 else
704 {
705 RtlCopyMemory(
706 Irp->AssociatedIrp.SystemBuffer,
707 &DeviceExtension->KeyboardIndicators,
708 sizeof(KEYBOARD_INDICATOR_PARAMETERS));
709 Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
710 Status = STATUS_SUCCESS;
711 }
712 break;
713 }
714 case IOCTL_KEYBOARD_SET_INDICATORS:
715 {
716 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_SET_INDICATORS\n");
717
718 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS))
719 {
720 Status = STATUS_BUFFER_TOO_SMALL;
721 }
722 else
723 {
724 RtlCopyMemory(
725 &DeviceExtension->KeyboardIndicators,
726 Irp->AssociatedIrp.SystemBuffer,
727 sizeof(KEYBOARD_INDICATOR_PARAMETERS));
728 Status = STATUS_PENDING;
729 IoMarkIrpPending(Irp);
730 IoStartPacket(DeviceObject, Irp, NULL, NULL);
731 }
732 break;
733 }
734 default:
735 {
736 ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
737 Stack->Parameters.DeviceIoControl.IoControlCode);
738 ASSERT(FALSE);
739 return ForwardIrpAndForget(DeviceObject, Irp);
740 }
741 }
742
743 if (Status != STATUS_PENDING)
744 {
745 Irp->IoStatus.Status = Status;
746 IoCompleteRequest(Irp, IO_NO_INCREMENT);
747 }
748 return Status;
749 }
750
751 /*
752 * Call the customization hook. The ToReturn parameter is about whether
753 * we should go on with the interrupt. The return value is what
754 * we should return (indicating to the system whether someone else
755 * should try to handle the interrupt)
756 */
757 static BOOLEAN
i8042KbdCallIsrHook(IN PI8042_KEYBOARD_EXTENSION DeviceExtension,IN UCHAR Status,IN UCHAR Input,OUT PBOOLEAN ToReturn)758 i8042KbdCallIsrHook(
759 IN PI8042_KEYBOARD_EXTENSION DeviceExtension,
760 IN UCHAR Status,
761 IN UCHAR Input,
762 OUT PBOOLEAN ToReturn)
763 {
764 BOOLEAN HookReturn, HookContinue;
765
766 HookContinue = FALSE;
767
768 if (DeviceExtension->KeyboardHook.IsrRoutine)
769 {
770 HookReturn = DeviceExtension->KeyboardHook.IsrRoutine(
771 DeviceExtension->KeyboardHook.Context,
772 DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer,
773 &DeviceExtension->Common.PortDeviceExtension->Packet,
774 Status,
775 &Input,
776 &HookContinue,
777 &DeviceExtension->KeyboardScanState);
778
779 if (!HookContinue)
780 {
781 *ToReturn = HookReturn;
782 return TRUE;
783 }
784 }
785 return FALSE;
786 }
787
788 BOOLEAN NTAPI
i8042KbdInterruptService(IN PKINTERRUPT Interrupt,PVOID Context)789 i8042KbdInterruptService(
790 IN PKINTERRUPT Interrupt,
791 PVOID Context)
792 {
793 PI8042_KEYBOARD_EXTENSION DeviceExtension;
794 PPORT_DEVICE_EXTENSION PortDeviceExtension;
795 PKEYBOARD_INPUT_DATA InputData;
796 ULONG Counter;
797 UCHAR PortStatus = 0, Output = 0;
798 BOOLEAN ToReturn = FALSE;
799 NTSTATUS Status;
800
801 UNREFERENCED_PARAMETER(Interrupt);
802
803 __analysis_assume(Context != NULL);
804 DeviceExtension = Context;
805 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
806 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer;
807 Counter = PortDeviceExtension->Settings.PollStatusIterations;
808
809 while (Counter)
810 {
811 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus);
812 if (!NT_SUCCESS(Status))
813 {
814 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status);
815 return FALSE;
816 }
817 Status = i8042ReadKeyboardData(PortDeviceExtension, &Output);
818 if (NT_SUCCESS(Status))
819 break;
820 KeStallExecutionProcessor(1);
821 Counter--;
822 }
823 if (Counter == 0)
824 {
825 WARN_(I8042PRT, "Spurious i8042 keyboard interrupt\n");
826 return FALSE;
827 }
828
829 INFO_(I8042PRT, "Got: 0x%02x\n", Output);
830
831 if (PortDeviceExtension->Settings.CrashOnCtrlScroll)
832 {
833 /* Test for CTRL + SCROLL LOCK twice */
834 static const UCHAR ScanCodes[] = { 0x1d, 0x46, 0xc6, 0x46, 0 };
835
836 if (Output == ScanCodes[DeviceExtension->ComboPosition])
837 {
838 DeviceExtension->ComboPosition++;
839 if (ScanCodes[DeviceExtension->ComboPosition] == 0)
840 KeBugCheck(MANUALLY_INITIATED_CRASH);
841 }
842 else if (Output == 0xfa)
843 {
844 /* Ignore ACK */
845 }
846 else if (Output == ScanCodes[0])
847 DeviceExtension->ComboPosition = 1;
848 else
849 DeviceExtension->ComboPosition = 0;
850
851 /* Test for TAB + key combination */
852 if (InputData->MakeCode == 0x0F)
853 DeviceExtension->TabPressed = !(InputData->Flags & KEY_BREAK);
854 else if (DeviceExtension->TabPressed)
855 {
856 DeviceExtension->TabPressed = FALSE;
857
858 /* Check which action to do */
859 if (InputData->MakeCode == 0x25)
860 {
861 /* k - Breakpoint */
862 DbgBreakPointWithStatus(DBG_STATUS_SYSRQ);
863 }
864 else if (InputData->MakeCode == 0x30)
865 {
866 /* b - Bugcheck */
867 KeBugCheck(MANUALLY_INITIATED_CRASH);
868 }
869 else
870 {
871 /* Send request to the kernel debugger.
872 * Unknown requests will be ignored. */
873 KdSystemDebugControl(' soR',
874 (PVOID)(ULONG_PTR)InputData->MakeCode,
875 0,
876 NULL,
877 0,
878 NULL,
879 KernelMode);
880 }
881 }
882 }
883
884 if (i8042KbdCallIsrHook(DeviceExtension, PortStatus, Output, &ToReturn))
885 return ToReturn;
886
887 if (i8042PacketIsr(PortDeviceExtension, Output))
888 {
889 if (PortDeviceExtension->PacketComplete)
890 {
891 TRACE_(I8042PRT, "Packet complete\n");
892 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL);
893 }
894 TRACE_(I8042PRT, "Irq eaten by packet\n");
895 return TRUE;
896 }
897
898 TRACE_(I8042PRT, "Irq is keyboard input\n");
899
900 if (DeviceExtension->KeyboardScanState == Normal)
901 {
902 switch (Output)
903 {
904 case 0xe0:
905 DeviceExtension->KeyboardScanState = GotE0;
906 return TRUE;
907 case 0xe1:
908 DeviceExtension->KeyboardScanState = GotE1;
909 return TRUE;
910 default:
911 break;
912 }
913 }
914
915 /* Update InputData */
916 InputData->Flags = 0;
917 switch (DeviceExtension->KeyboardScanState)
918 {
919 case GotE0:
920 InputData->Flags |= KEY_E0;
921 break;
922 case GotE1:
923 InputData->Flags |= KEY_E1;
924 break;
925 default:
926 break;
927 }
928 DeviceExtension->KeyboardScanState = Normal;
929 if (Output & 0x80)
930 InputData->Flags |= KEY_BREAK;
931 else
932 InputData->Flags |= KEY_MAKE;
933 InputData->MakeCode = Output & 0x7f;
934 InputData->Reserved = 0;
935
936 DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext);
937
938 return TRUE;
939 }
940