xref: /reactos/drivers/input/i8042prt/keyboard.c (revision ea6d427d)
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
i8042KbdQueuePacket(IN PVOID Context)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
i8042SynchWritePortKbd(IN PVOID Context,IN UCHAR Value,IN BOOLEAN WaitForAck)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
i8042KbdStartIo(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
i8042PacketDpc(IN PPORT_DEVICE_EXTENSION DeviceExtension)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
i8042PowerWorkItem(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)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
HandlePowerKeys(IN PI8042_KEYBOARD_EXTENSION DeviceExtension)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
i8042KbdDpcRoutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)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
i8042KbdDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
i8042InitializeKeyboardAttributes(PI8042_KEYBOARD_EXTENSION DeviceExtension)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
i8042KbdInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
i8042KbdCallIsrHook(IN PI8042_KEYBOARD_EXTENSION DeviceExtension,IN UCHAR Status,IN UCHAR Input,OUT PBOOLEAN ToReturn)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
i8042KbdInterruptService(IN PKINTERRUPT Interrupt,PVOID Context)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             else
870             {
871 			    /* Send request to the kernel debugger.
872 			     * Unknown requests will be ignored. */
873 			    KdSystemDebugControl(' soR',
874 			                         (PVOID)(ULONG_PTR)InputData->MakeCode,
875 			                         0,
876 			                         NULL,
877 			                         0,
878 			                         NULL,
879 			                         KernelMode);
880             }
881 		}
882 	}
883 
884 	if (i8042KbdCallIsrHook(DeviceExtension, PortStatus, Output, &ToReturn))
885 		return ToReturn;
886 
887 	if (i8042PacketIsr(PortDeviceExtension, Output))
888 	{
889 		if (PortDeviceExtension->PacketComplete)
890 		{
891 			TRACE_(I8042PRT, "Packet complete\n");
892 			KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL);
893 		}
894 		TRACE_(I8042PRT, "Irq eaten by packet\n");
895 		return TRUE;
896 	}
897 
898 	TRACE_(I8042PRT, "Irq is keyboard input\n");
899 
900 	if (DeviceExtension->KeyboardScanState == Normal)
901 	{
902 		switch (Output)
903 		{
904 			case 0xe0:
905 				DeviceExtension->KeyboardScanState = GotE0;
906 				return TRUE;
907 			case 0xe1:
908 				DeviceExtension->KeyboardScanState = GotE1;
909 				return TRUE;
910 			default:
911 				break;
912 		}
913 	}
914 
915 	/* Update InputData */
916 	InputData->Flags = 0;
917 	switch (DeviceExtension->KeyboardScanState)
918 	{
919 		case GotE0:
920 			InputData->Flags |= KEY_E0;
921 			break;
922 		case GotE1:
923 			InputData->Flags |= KEY_E1;
924 			break;
925 		default:
926 			break;
927 	}
928 	DeviceExtension->KeyboardScanState = Normal;
929 	if (Output & 0x80)
930 		InputData->Flags |= KEY_BREAK;
931 	else
932 		InputData->Flags |= KEY_MAKE;
933 	InputData->MakeCode = Output & 0x7f;
934 	InputData->Reserved = 0;
935 
936 	DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext);
937 
938 	return TRUE;
939 }
940