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