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