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