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