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