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