xref: /reactos/drivers/input/i8042prt/pnp.c (revision c501d811)
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/pnp.c
5  * PURPOSE:     IRP_MJ_PNP operations
6  * PROGRAMMERS: Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org)
7  *              Copyright 2008 Colin Finck (mail@colinfinck.de)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include "i8042prt.h"
13 
14 /* FUNCTIONS *****************************************************************/
15 
16 /* This is all pretty confusing. There's more than one way to
17  * disable/enable the keyboard. You can send KBD_ENABLE to the
18  * keyboard, and it will start scanning keys. Sending KBD_DISABLE
19  * will disable the key scanning but also reset the parameters to
20  * defaults.
21  *
22  * You can also send 0xAE to the controller for enabling the
23  * keyboard clock line and 0xAD for disabling it. Then it'll
24  * automatically get turned on at the next command. The last
25  * way is by modifying the bit that drives the clock line in the
26  * 'command byte' of the controller. This is almost, but not quite,
27  * the same as the AE/AD thing. The difference can be used to detect
28  * some really old broken keyboard controllers which I hope won't be
29  * necessary.
30  *
31  * We change the command byte, sending KBD_ENABLE/DISABLE seems to confuse
32  * some kvm switches.
33  */
34 BOOLEAN
35 i8042ChangeMode(
36 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
37 	IN UCHAR FlagsToDisable,
38 	IN UCHAR FlagsToEnable)
39 {
40 	UCHAR Value;
41 	NTSTATUS Status;
42 
43 	if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, KBD_READ_MODE))
44 	{
45 		WARN_(I8042PRT, "Can't read i8042 mode\n");
46 		return FALSE;
47 	}
48 
49 	Status = i8042ReadDataWait(DeviceExtension, &Value);
50 	if (!NT_SUCCESS(Status))
51 	{
52 		WARN_(I8042PRT, "No response after read i8042 mode\n");
53 		return FALSE;
54 	}
55 
56 	Value &= ~FlagsToDisable;
57 	Value |= FlagsToEnable;
58 
59 	if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, KBD_WRITE_MODE))
60 	{
61 		WARN_(I8042PRT, "Can't set i8042 mode\n");
62 		return FALSE;
63 	}
64 
65 	if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value))
66 	{
67 		WARN_(I8042PRT, "Can't send i8042 mode\n");
68 		return FALSE;
69 	}
70 
71 	return TRUE;
72 }
73 
74 static NTSTATUS
75 i8042BasicDetect(
76 	IN PPORT_DEVICE_EXTENSION DeviceExtension)
77 {
78 	NTSTATUS Status;
79 	ULONG ResendIterations;
80 	UCHAR Value = 0;
81 
82 	/* Don't enable keyboard and mouse interrupts, disable keyboard/mouse */
83 	if (!i8042ChangeMode(DeviceExtension, CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB, CCB_KBD_DISAB | CCB_MOUSE_DISAB))
84 		return STATUS_IO_DEVICE_ERROR;
85 
86 	i8042Flush(DeviceExtension);
87 
88 	ResendIterations = DeviceExtension->Settings.ResendIterations + 1;
89 	while (ResendIterations--)
90 	{
91 		if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST))
92 		{
93 			WARN_(I8042PRT, "Writing CTRL_SELF_TEST command failed\n");
94 			return STATUS_IO_TIMEOUT;
95 		}
96 
97 		Status = i8042ReadDataWait(DeviceExtension, &Value);
98 		if (!NT_SUCCESS(Status))
99 		{
100 			WARN_(I8042PRT, "Failed to read CTRL_SELF_TEST response, status 0x%08lx\n", Status);
101 			return Status;
102 		}
103 
104 		if (Value == KBD_SELF_TEST_OK)
105 		{
106 			INFO_(I8042PRT, "CTRL_SELF_TEST completed successfully!\n");
107 			break;
108 		}
109 		else if (Value == KBD_RESEND)
110 		{
111 			TRACE_(I8042PRT, "Resending...\n", Value);
112 			KeStallExecutionProcessor(50);
113 		}
114 		else
115 		{
116 			WARN_(I8042PRT, "Got 0x%02x instead of 0x55\n", Value);
117 			return STATUS_IO_DEVICE_ERROR;
118 		}
119 	}
120 
121 	/*
122 	 * We used to send a KBD_LINE_TEST (0xAB) command here, but on at least HP
123 	 * Pavilion notebooks the response to that command was incorrect.
124 	 * So now we just assume that a keyboard is attached.
125 	 */
126 	DeviceExtension->Flags |= KEYBOARD_PRESENT;
127 
128 	if (i8042Write(DeviceExtension, DeviceExtension->ControlPort, MOUSE_LINE_TEST))
129 	{
130 		Status = i8042ReadDataWait(DeviceExtension, &Value);
131 		if (NT_SUCCESS(Status) && Value == 0)
132 			DeviceExtension->Flags |= MOUSE_PRESENT;
133 	}
134 
135 	if (IsFirstStageSetup())
136 		/* Ignore the mouse */
137 		DeviceExtension->Flags &= ~MOUSE_PRESENT;
138 
139 	return STATUS_SUCCESS;
140 }
141 
142 static VOID
143 i8042DetectKeyboard(
144 	IN PPORT_DEVICE_EXTENSION DeviceExtension)
145 {
146 	NTSTATUS Status;
147 
148 	/* Set LEDs (that is not fatal if some error occurs) */
149 	Status = i8042SynchWritePort(DeviceExtension, 0, KBD_CMD_SET_LEDS, TRUE);
150 	if (NT_SUCCESS(Status))
151 	{
152 		Status = i8042SynchWritePort(DeviceExtension, 0, 0, TRUE);
153 		if (!NT_SUCCESS(Status))
154 		{
155 			WARN_(I8042PRT, "Can't finish SET_LEDS (0x%08lx)\n", Status);
156 			return;
157 		}
158 	}
159 	else
160 	{
161 		WARN_(I8042PRT, "Warning: can't write SET_LEDS (0x%08lx)\n", Status);
162 	}
163 
164 	/* Turn on translation and SF (Some machines don't reboot if SF is not set, see ReactOS bug #1842) */
165 	if (!i8042ChangeMode(DeviceExtension, 0, CCB_TRANSLATE | CCB_SYSTEM_FLAG))
166 		return;
167 
168 	INFO_(I8042PRT, "Keyboard detected\n");
169 }
170 
171 static VOID
172 i8042DetectMouse(
173 	IN PPORT_DEVICE_EXTENSION DeviceExtension)
174 {
175 	NTSTATUS Status;
176 	UCHAR Value;
177 	UCHAR ExpectedReply[] = { MOUSE_ACK, 0xAA };
178 	UCHAR ReplyByte;
179 
180 	i8042Flush(DeviceExtension);
181 
182 	if(!i8042IsrWritePort(DeviceExtension, MOU_CMD_RESET, CTRL_WRITE_MOUSE))
183 	{
184 		WARN_(I8042PRT, "Failed to write reset command to mouse\n");
185 		goto failure;
186 	}
187 
188 	/* The implementation of the "Mouse Reset" command differs much from chip to chip.
189 
190 	   By default, the first byte is an ACK, when the mouse is plugged in and working and NACK when it's not.
191 	   On success, the next bytes are 0xAA and 0x00.
192 
193 	   But on some systems (like ECS K7S5A Pro, SiS 735 chipset), we always get an ACK and 0xAA.
194 	   Only the last byte indicates, whether a mouse is plugged in.
195 	   It is either sent or not, so there is no byte, which indicates a failure here.
196 
197 	   After the Mouse Reset command was issued, it usually takes some time until we get a response.
198 	   So get the first two bytes in a loop. */
199 	for (ReplyByte = 0;
200 	     ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]);
201 	     ReplyByte++)
202 	{
203 		ULONG Counter = 500;
204 
205 		do
206 		{
207 			Status = i8042ReadDataWait(DeviceExtension, &Value);
208 
209 			if(!NT_SUCCESS(Status))
210 			{
211 				/* Wait some time before trying again */
212 				KeStallExecutionProcessor(50);
213 			}
214 		} while (Status == STATUS_IO_TIMEOUT && Counter--);
215 
216 		if (!NT_SUCCESS(Status))
217 		{
218 			WARN_(I8042PRT, "No ACK after mouse reset, status 0x%08lx\n", Status);
219 			goto failure;
220 		}
221 		else if (Value != ExpectedReply[ReplyByte])
222 		{
223 			WARN_(I8042PRT, "Unexpected reply: 0x%02x (expected 0x%02x)\n", Value, ExpectedReply[ReplyByte]);
224 			goto failure;
225 		}
226 	}
227 
228 	/* Finally get the third byte, but only try it one time (see above).
229 	   Otherwise this takes around 45 seconds on a K7S5A Pro, when no mouse is plugged in. */
230 	Status = i8042ReadDataWait(DeviceExtension, &Value);
231 
232 	if(!NT_SUCCESS(Status))
233 	{
234 		WARN_(I8042PRT, "Last byte was not transmitted after mouse reset, status 0x%08lx\n", Status);
235 		goto failure;
236 	}
237 	else if(Value != 0x00)
238 	{
239 		WARN_(I8042PRT, "Last byte after mouse reset was not 0x00, but 0x%02x\n", Value);
240 		goto failure;
241 	}
242 
243 	INFO_(I8042PRT, "Mouse detected\n");
244 	return;
245 
246 failure:
247 	/* There is probably no mouse present. On some systems,
248 	   the probe locks the entire keyboard controller. Let's
249 	   try to get access to the keyboard again by sending a
250 	   reset */
251 	i8042Flush(DeviceExtension);
252 	i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST);
253 	i8042ReadDataWait(DeviceExtension, &Value);
254 	i8042Flush(DeviceExtension);
255 
256 	INFO_(I8042PRT, "Mouse not detected\n");
257 }
258 
259 static NTSTATUS
260 i8042ConnectKeyboardInterrupt(
261 	IN PI8042_KEYBOARD_EXTENSION DeviceExtension)
262 {
263 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
264 	KIRQL DirqlMax;
265 	NTSTATUS Status;
266 
267 	TRACE_(I8042PRT, "i8042ConnectKeyboardInterrupt()\n");
268 
269 	PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
270 	DirqlMax = MAX(
271 		PortDeviceExtension->KeyboardInterrupt.Dirql,
272 		PortDeviceExtension->MouseInterrupt.Dirql);
273 
274 	INFO_(I8042PRT, "KeyboardInterrupt.Vector         %lu\n",
275 		PortDeviceExtension->KeyboardInterrupt.Vector);
276 	INFO_(I8042PRT, "KeyboardInterrupt.Dirql          %lu\n",
277 		PortDeviceExtension->KeyboardInterrupt.Dirql);
278 	INFO_(I8042PRT, "KeyboardInterrupt.DirqlMax       %lu\n",
279 		DirqlMax);
280 	INFO_(I8042PRT, "KeyboardInterrupt.InterruptMode  %s\n",
281 		PortDeviceExtension->KeyboardInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched");
282 	INFO_(I8042PRT, "KeyboardInterrupt.ShareInterrupt %s\n",
283 		PortDeviceExtension->KeyboardInterrupt.ShareInterrupt ? "yes" : "no");
284 	INFO_(I8042PRT, "KeyboardInterrupt.Affinity       0x%lx\n",
285 		PortDeviceExtension->KeyboardInterrupt.Affinity);
286 	Status = IoConnectInterrupt(
287 		&PortDeviceExtension->KeyboardInterrupt.Object,
288 		i8042KbdInterruptService,
289 		DeviceExtension, &PortDeviceExtension->SpinLock,
290 		PortDeviceExtension->KeyboardInterrupt.Vector, PortDeviceExtension->KeyboardInterrupt.Dirql, DirqlMax,
291 		PortDeviceExtension->KeyboardInterrupt.InterruptMode, PortDeviceExtension->KeyboardInterrupt.ShareInterrupt,
292 		PortDeviceExtension->KeyboardInterrupt.Affinity, FALSE);
293 	if (!NT_SUCCESS(Status))
294 	{
295 		WARN_(I8042PRT, "IoConnectInterrupt() failed with status 0x%08x\n", Status);
296 		return Status;
297 	}
298 
299 	if (DirqlMax == PortDeviceExtension->KeyboardInterrupt.Dirql)
300 		PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object;
301 	PortDeviceExtension->Flags |= KEYBOARD_INITIALIZED;
302 	return STATUS_SUCCESS;
303 }
304 
305 static NTSTATUS
306 i8042ConnectMouseInterrupt(
307 	IN PI8042_MOUSE_EXTENSION DeviceExtension)
308 {
309 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
310 	KIRQL DirqlMax;
311 	NTSTATUS Status;
312 
313 	TRACE_(I8042PRT, "i8042ConnectMouseInterrupt()\n");
314 
315 	Status = i8042MouInitialize(DeviceExtension);
316 	if (!NT_SUCCESS(Status))
317 		return Status;
318 
319 	PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
320 	DirqlMax = MAX(
321 		PortDeviceExtension->KeyboardInterrupt.Dirql,
322 		PortDeviceExtension->MouseInterrupt.Dirql);
323 
324 	INFO_(I8042PRT, "MouseInterrupt.Vector         %lu\n",
325 		PortDeviceExtension->MouseInterrupt.Vector);
326 	INFO_(I8042PRT, "MouseInterrupt.Dirql          %lu\n",
327 		PortDeviceExtension->MouseInterrupt.Dirql);
328 	INFO_(I8042PRT, "MouseInterrupt.DirqlMax       %lu\n",
329 		DirqlMax);
330 	INFO_(I8042PRT, "MouseInterrupt.InterruptMode  %s\n",
331 		PortDeviceExtension->MouseInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched");
332 	INFO_(I8042PRT, "MouseInterrupt.ShareInterrupt %s\n",
333 		PortDeviceExtension->MouseInterrupt.ShareInterrupt ? "yes" : "no");
334 	INFO_(I8042PRT, "MouseInterrupt.Affinity       0x%lx\n",
335 		PortDeviceExtension->MouseInterrupt.Affinity);
336 	Status = IoConnectInterrupt(
337 		&PortDeviceExtension->MouseInterrupt.Object,
338 		i8042MouInterruptService,
339 		DeviceExtension, &PortDeviceExtension->SpinLock,
340 		PortDeviceExtension->MouseInterrupt.Vector, PortDeviceExtension->MouseInterrupt.Dirql, DirqlMax,
341 		PortDeviceExtension->MouseInterrupt.InterruptMode, PortDeviceExtension->MouseInterrupt.ShareInterrupt,
342 		PortDeviceExtension->MouseInterrupt.Affinity, FALSE);
343 	if (!NT_SUCCESS(Status))
344 	{
345 		WARN_(I8042PRT, "IoConnectInterrupt() failed with status 0x%08x\n", Status);
346 		goto cleanup;
347 	}
348 
349 	if (DirqlMax == PortDeviceExtension->MouseInterrupt.Dirql)
350 		PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->MouseInterrupt.Object;
351 
352 	PortDeviceExtension->Flags |= MOUSE_INITIALIZED;
353 	Status = STATUS_SUCCESS;
354 
355 cleanup:
356 	if (!NT_SUCCESS(Status))
357 	{
358 		PortDeviceExtension->Flags &= ~MOUSE_INITIALIZED;
359 		if (PortDeviceExtension->MouseInterrupt.Object)
360 		{
361 			IoDisconnectInterrupt(PortDeviceExtension->MouseInterrupt.Object);
362 			PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object;
363 		}
364 	}
365 	return Status;
366 }
367 
368 static NTSTATUS
369 EnableInterrupts(
370 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
371 	IN UCHAR FlagsToDisable,
372 	IN UCHAR FlagsToEnable)
373 {
374 	i8042Flush(DeviceExtension);
375 
376 	if (!i8042ChangeMode(DeviceExtension, FlagsToDisable, FlagsToEnable))
377 		return STATUS_UNSUCCESSFUL;
378 
379 	/* Reset the mouse (if any) to start the detection */
380 	if (DeviceExtension->Flags & MOUSE_PRESENT)
381 	{
382 		KIRQL Irql;
383 
384 		Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt);
385 		i8042IsrWritePort(DeviceExtension, MOU_CMD_RESET, CTRL_WRITE_MOUSE);
386 		KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql);
387 	}
388 
389 	return STATUS_SUCCESS;
390 }
391 
392 static NTSTATUS
393 StartProcedure(
394 	IN PPORT_DEVICE_EXTENSION DeviceExtension)
395 {
396 	NTSTATUS Status;
397 	UCHAR FlagsToDisable = 0;
398 	UCHAR FlagsToEnable = 0;
399 
400 	if (DeviceExtension->DataPort == 0)
401 	{
402 		/* Unable to do something at the moment */
403 		return STATUS_SUCCESS;
404 	}
405 
406 	if (!(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_PRESENT)))
407 	{
408 		/* Try to detect them */
409 		TRACE_(I8042PRT, "Check if the controller is really a i8042\n");
410 		Status = i8042BasicDetect(DeviceExtension);
411 		if (!NT_SUCCESS(Status))
412 		{
413 			WARN_(I8042PRT, "i8042BasicDetect() failed with status 0x%08lx\n", Status);
414 			return STATUS_UNSUCCESSFUL;
415 		}
416 
417 		/* First detect the mouse and then the keyboard!
418 		   If we do it the other way round, some systems throw away settings like the keyboard translation, when detecting the mouse. */
419 		TRACE_(I8042PRT, "Detecting mouse\n");
420 		i8042DetectMouse(DeviceExtension);
421 
422 		TRACE_(I8042PRT, "Detecting keyboard\n");
423 		i8042DetectKeyboard(DeviceExtension);
424 
425 		INFO_(I8042PRT, "Keyboard present: %s\n", DeviceExtension->Flags & KEYBOARD_PRESENT ? "YES" : "NO");
426 		INFO_(I8042PRT, "Mouse present   : %s\n", DeviceExtension->Flags & MOUSE_PRESENT ? "YES" : "NO");
427 	}
428 
429 	/* Connect interrupts */
430 	if (DeviceExtension->Flags & KEYBOARD_PRESENT &&
431 	    DeviceExtension->Flags & KEYBOARD_CONNECTED &&
432 	    DeviceExtension->Flags & KEYBOARD_STARTED &&
433 	    !(DeviceExtension->Flags & KEYBOARD_INITIALIZED))
434 	{
435 		/* Keyboard is ready to be initialized */
436 		Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension);
437 		if (NT_SUCCESS(Status))
438 		{
439 			DeviceExtension->Flags |= KEYBOARD_INITIALIZED;
440 			FlagsToDisable |= CCB_KBD_DISAB;
441 			FlagsToEnable |= CCB_KBD_INT_ENAB;
442 		}
443 	}
444 
445 	if (DeviceExtension->Flags & MOUSE_PRESENT &&
446 	    DeviceExtension->Flags & MOUSE_CONNECTED &&
447 	    DeviceExtension->Flags & MOUSE_STARTED &&
448 	    !(DeviceExtension->Flags & MOUSE_INITIALIZED))
449 	{
450 		/* Mouse is ready to be initialized */
451 		Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension);
452 		if (NT_SUCCESS(Status))
453 		{
454 			DeviceExtension->Flags |= MOUSE_INITIALIZED;
455 			FlagsToDisable |= CCB_MOUSE_DISAB;
456 			FlagsToEnable |= CCB_MOUSE_INT_ENAB;
457 		}
458 	}
459 
460 	if (FlagsToEnable)
461 		Status = EnableInterrupts(DeviceExtension, FlagsToDisable, FlagsToEnable);
462 	else
463 		Status = STATUS_SUCCESS;
464 
465 	return Status;
466 }
467 
468 static NTSTATUS
469 i8042PnpStartDevice(
470 	IN PDEVICE_OBJECT DeviceObject,
471 	IN PCM_RESOURCE_LIST AllocatedResources,
472 	IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
473 {
474 	PFDO_DEVICE_EXTENSION DeviceExtension;
475 	PPORT_DEVICE_EXTENSION PortDeviceExtension;
476 	PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated;
477 	INTERRUPT_DATA InterruptData;
478 	BOOLEAN FoundDataPort = FALSE;
479 	BOOLEAN FoundControlPort = FALSE;
480 	BOOLEAN FoundIrq = FALSE;
481 	ULONG i;
482 	NTSTATUS Status;
483 
484 	TRACE_(I8042PRT, "i8042PnpStartDevice(%p)\n", DeviceObject);
485 	DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
486 	PortDeviceExtension = DeviceExtension->PortDeviceExtension;
487 
488 	ASSERT(DeviceExtension->PnpState == dsStopped);
489 
490 	if (!AllocatedResources)
491 	{
492 		WARN_(I8042PRT, "No allocated resources sent to driver\n");
493 		return STATUS_INSUFFICIENT_RESOURCES;
494 	}
495 	if (AllocatedResources->Count != 1)
496 	{
497 		WARN_(I8042PRT, "Wrong number of allocated resources sent to driver\n");
498 		return STATUS_INSUFFICIENT_RESOURCES;
499 	}
500 	if (AllocatedResources->List[0].PartialResourceList.Version != 1
501 	 || AllocatedResources->List[0].PartialResourceList.Revision != 1
502 	 || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1
503 	 || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1)
504 	{
505 		WARN_(I8042PRT, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
506 			AllocatedResources->List[0].PartialResourceList.Version,
507 			AllocatedResources->List[0].PartialResourceList.Revision,
508 			AllocatedResourcesTranslated->List[0].PartialResourceList.Version,
509 			AllocatedResourcesTranslated->List[0].PartialResourceList.Revision);
510 		return STATUS_REVISION_MISMATCH;
511 	}
512 
513 	/* Get Irq and optionally control port and data port */
514 	for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
515 	{
516 		ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
517 		ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];
518 		switch (ResourceDescriptor->Type)
519 		{
520 			case CmResourceTypePort:
521 			{
522 				if (ResourceDescriptor->u.Port.Length == 1)
523 				{
524 					/* We assume that the first ressource will
525 					 * be the control port and the second one
526 					 * will be the data port...
527 					 */
528 					if (!FoundDataPort)
529 					{
530 						PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
531 						INFO_(I8042PRT, "Found data port: %p\n", PortDeviceExtension->DataPort);
532 						FoundDataPort = TRUE;
533 					}
534 					else if (!FoundControlPort)
535 					{
536 						PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
537 						INFO_(I8042PRT, "Found control port: %p\n", PortDeviceExtension->ControlPort);
538 						FoundControlPort = TRUE;
539 					}
540 					else
541 					{
542 						WARN_(I8042PRT, "Too much I/O ranges provided: 0x%lx\n", ResourceDescriptor->u.Port.Length);
543 						return STATUS_INVALID_PARAMETER;
544 					}
545 				}
546 				else
547 					WARN_(I8042PRT, "Invalid I/O range length: 0x%lx\n", ResourceDescriptor->u.Port.Length);
548 				break;
549 			}
550 			case CmResourceTypeInterrupt:
551 			{
552 				if (FoundIrq)
553 					return STATUS_INVALID_PARAMETER;
554 				InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level;
555 				InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector;
556 				InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity;
557 				if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
558 					InterruptData.InterruptMode = Latched;
559 				else
560 					InterruptData.InterruptMode = LevelSensitive;
561 				InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared);
562 				INFO_(I8042PRT, "Found irq resource: %lu\n", ResourceDescriptor->u.Interrupt.Level);
563 				FoundIrq = TRUE;
564 				break;
565 			}
566 			default:
567 				WARN_(I8042PRT, "Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
568 		}
569 	}
570 
571 	if (!FoundIrq)
572 	{
573 		WARN_(I8042PRT, "Interrupt resource was not found in allocated resources list\n");
574 		return STATUS_INSUFFICIENT_RESOURCES;
575 	}
576 	else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort))
577 	{
578 		WARN_(I8042PRT, "Some required resources were not found in allocated resources list\n");
579 		return STATUS_INSUFFICIENT_RESOURCES;
580 	}
581 	else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort))
582 	{
583 		WARN_(I8042PRT, "Too much resources were provided in allocated resources list\n");
584 		return STATUS_INVALID_PARAMETER;
585 	}
586 
587 	switch (DeviceExtension->Type)
588 	{
589 		case Keyboard:
590 		{
591 			RtlCopyMemory(
592 				&PortDeviceExtension->KeyboardInterrupt,
593 				&InterruptData,
594 				sizeof(INTERRUPT_DATA));
595 			PortDeviceExtension->Flags |= KEYBOARD_STARTED;
596 			Status = StartProcedure(PortDeviceExtension);
597 			break;
598 		}
599 		case Mouse:
600 		{
601 			RtlCopyMemory(
602 				&PortDeviceExtension->MouseInterrupt,
603 				&InterruptData,
604 				sizeof(INTERRUPT_DATA));
605 			PortDeviceExtension->Flags |= MOUSE_STARTED;
606 			Status = StartProcedure(PortDeviceExtension);
607 			break;
608 		}
609 		default:
610 		{
611 			ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type);
612 			ASSERT(FALSE);
613 			Status = STATUS_INVALID_DEVICE_REQUEST;
614 		}
615 	}
616 
617 	if (NT_SUCCESS(Status))
618 		DeviceExtension->PnpState = dsStarted;
619 
620 	return Status;
621 }
622 
623 NTSTATUS NTAPI
624 i8042Pnp(
625 	IN PDEVICE_OBJECT DeviceObject,
626 	IN PIRP Irp)
627 {
628 	PIO_STACK_LOCATION Stack;
629 	ULONG MinorFunction;
630 	I8042_DEVICE_TYPE DeviceType;
631 	ULONG_PTR Information = 0;
632 	NTSTATUS Status;
633 
634 	Stack = IoGetCurrentIrpStackLocation(Irp);
635 	MinorFunction = Stack->MinorFunction;
636 	DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type;
637 
638 	switch (MinorFunction)
639 	{
640 		case IRP_MN_START_DEVICE: /* 0x00 */
641 		{
642 			TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
643 
644 			/* Call lower driver (if any) */
645 			if (DeviceType != PhysicalDeviceObject)
646 			{
647 				Status = ForwardIrpAndWait(DeviceObject, Irp);
648 				if (NT_SUCCESS(Status))
649 					Status = i8042PnpStartDevice(
650 						DeviceObject,
651 						Stack->Parameters.StartDevice.AllocatedResources,
652 						Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
653 			}
654 			else
655 				Status = STATUS_SUCCESS;
656 			break;
657 		}
658 		case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */
659 		{
660 			switch (Stack->Parameters.QueryDeviceRelations.Type)
661 			{
662 				case BusRelations:
663 				{
664 					PDEVICE_RELATIONS DeviceRelations;
665 
666 					TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
667 					DeviceRelations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), I8042PRT_TAG);
668 					if (DeviceRelations)
669 					{
670 						DeviceRelations->Count = 0;
671 						Information = (ULONG_PTR)DeviceRelations;
672 						Status = STATUS_SUCCESS;
673 					}
674 					else
675 						Status = STATUS_INSUFFICIENT_RESOURCES;
676 					break;
677 				}
678 				case RemovalRelations:
679 				{
680 					TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
681 					return ForwardIrpAndForget(DeviceObject, Irp);
682 				}
683 				default:
684 					ERR_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
685 						Stack->Parameters.QueryDeviceRelations.Type);
686 					ASSERT(FALSE);
687 					return ForwardIrpAndForget(DeviceObject, Irp);
688 			}
689 			break;
690 		}
691 		case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */
692 		{
693 			TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
694 			/* Nothing to do */
695 			Status = Irp->IoStatus.Status;
696 			break;
697 		}
698 		default:
699 		{
700 			ERR_(I8042PRT, "IRP_MJ_PNP / unknown minor function 0x%x\n", MinorFunction);
701 			ASSERT(FALSE);
702 			return ForwardIrpAndForget(DeviceObject, Irp);
703 		}
704 	}
705 
706 	Irp->IoStatus.Information = Information;
707 	Irp->IoStatus.Status = Status;
708 	IoCompleteRequest(Irp, IO_NO_INCREMENT);
709 	return Status;
710 }
711