c70987b26f44ef13dac33b60bc672b1760c6acee
[reactos.git] / reactos / drivers / input / i8042prt / pnp.c
1 /*
2 * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/input/i8042prt/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 (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_WRITE_MOUSE)
183 ||!i8042Write(DeviceExtension, DeviceExtension->DataPort, MOU_CMD_RESET))
184 {
185 WARN_(I8042PRT, "Failed to write reset command to mouse\n");
186 goto failure;
187 }
188
189 /* The implementation of the "Mouse Reset" command differs much from chip to chip.
190
191 By default, the first byte is an ACK, when the mouse is plugged in and working and NACK when it's not.
192 On success, the next bytes are 0xAA and 0x00.
193
194 But on some systems (like ECS K7S5A Pro, SiS 735 chipset), we always get an ACK and 0xAA.
195 Only the last byte indicates, whether a mouse is plugged in.
196 It is either sent or not, so there is no byte, which indicates a failure here.
197
198 After the Mouse Reset command was issued, it usually takes some time until we get a response.
199 So get the first two bytes in a loop. */
200 for (ReplyByte = 0;
201 ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]);
202 ReplyByte++)
203 {
204 ULONG Counter = 500;
205
206 do
207 {
208 Status = i8042ReadDataWait(DeviceExtension, &Value);
209
210 if(!NT_SUCCESS(Status))
211 {
212 /* Wait some time before trying again */
213 KeStallExecutionProcessor(50);
214 }
215 } while (Status == STATUS_IO_TIMEOUT && Counter--);
216
217 if (!NT_SUCCESS(Status))
218 {
219 WARN_(I8042PRT, "No ACK after mouse reset, status 0x%08lx\n", Status);
220 goto failure;
221 }
222 else if (Value != ExpectedReply[ReplyByte])
223 {
224 WARN_(I8042PRT, "Unexpected reply: 0x%02x (expected 0x%02x)\n", Value, ExpectedReply[ReplyByte]);
225 goto failure;
226 }
227 }
228
229 /* Finally get the third byte, but only try it one time (see above).
230 Otherwise this takes around 45 seconds on a K7S5A Pro, when no mouse is plugged in. */
231 Status = i8042ReadDataWait(DeviceExtension, &Value);
232
233 if(!NT_SUCCESS(Status))
234 {
235 WARN_(I8042PRT, "Last byte was not transmitted after mouse reset, status 0x%08lx\n", Status);
236 goto failure;
237 }
238 else if(Value != 0x00)
239 {
240 WARN_(I8042PRT, "Last byte after mouse reset was not 0x00, but 0x%02x\n", Value);
241 goto failure;
242 }
243
244 INFO_(I8042PRT, "Mouse detected\n");
245 return;
246
247 failure:
248 /* There is probably no mouse present. On some systems,
249 the probe locks the entire keyboard controller. Let's
250 try to get access to the keyboard again by sending a
251 reset */
252 i8042Flush(DeviceExtension);
253 i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST);
254 i8042ReadDataWait(DeviceExtension, &Value);
255 i8042Flush(DeviceExtension);
256
257 INFO_(I8042PRT, "Mouse not detected\n");
258 }
259
260 static NTSTATUS
261 i8042ConnectKeyboardInterrupt(
262 IN PI8042_KEYBOARD_EXTENSION DeviceExtension)
263 {
264 PPORT_DEVICE_EXTENSION PortDeviceExtension;
265 KIRQL DirqlMax;
266 NTSTATUS Status;
267
268 TRACE_(I8042PRT, "i8042ConnectKeyboardInterrupt()\n");
269
270 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
271 DirqlMax = MAX(
272 PortDeviceExtension->KeyboardInterrupt.Dirql,
273 PortDeviceExtension->MouseInterrupt.Dirql);
274
275 INFO_(I8042PRT, "KeyboardInterrupt.Vector %lu\n",
276 PortDeviceExtension->KeyboardInterrupt.Vector);
277 INFO_(I8042PRT, "KeyboardInterrupt.Dirql %lu\n",
278 PortDeviceExtension->KeyboardInterrupt.Dirql);
279 INFO_(I8042PRT, "KeyboardInterrupt.DirqlMax %lu\n",
280 DirqlMax);
281 INFO_(I8042PRT, "KeyboardInterrupt.InterruptMode %s\n",
282 PortDeviceExtension->KeyboardInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched");
283 INFO_(I8042PRT, "KeyboardInterrupt.ShareInterrupt %s\n",
284 PortDeviceExtension->KeyboardInterrupt.ShareInterrupt ? "yes" : "no");
285 INFO_(I8042PRT, "KeyboardInterrupt.Affinity 0x%lx\n",
286 PortDeviceExtension->KeyboardInterrupt.Affinity);
287 Status = IoConnectInterrupt(
288 &PortDeviceExtension->KeyboardInterrupt.Object,
289 i8042KbdInterruptService,
290 DeviceExtension, &PortDeviceExtension->SpinLock,
291 PortDeviceExtension->KeyboardInterrupt.Vector, PortDeviceExtension->KeyboardInterrupt.Dirql, DirqlMax,
292 PortDeviceExtension->KeyboardInterrupt.InterruptMode, PortDeviceExtension->KeyboardInterrupt.ShareInterrupt,
293 PortDeviceExtension->KeyboardInterrupt.Affinity, FALSE);
294 if (!NT_SUCCESS(Status))
295 {
296 WARN_(I8042PRT, "IoConnectInterrupt() failed with status 0x%08x\n", Status);
297 return Status;
298 }
299
300 if (DirqlMax == PortDeviceExtension->KeyboardInterrupt.Dirql)
301 PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object;
302 PortDeviceExtension->Flags |= KEYBOARD_INITIALIZED;
303 return STATUS_SUCCESS;
304 }
305
306 static NTSTATUS
307 i8042ConnectMouseInterrupt(
308 IN PI8042_MOUSE_EXTENSION DeviceExtension)
309 {
310 PPORT_DEVICE_EXTENSION PortDeviceExtension;
311 KIRQL DirqlMax;
312 NTSTATUS Status;
313
314 TRACE_(I8042PRT, "i8042ConnectMouseInterrupt()\n");
315
316 Status = i8042MouInitialize(DeviceExtension);
317 if (!NT_SUCCESS(Status))
318 return Status;
319
320 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
321 DirqlMax = MAX(
322 PortDeviceExtension->KeyboardInterrupt.Dirql,
323 PortDeviceExtension->MouseInterrupt.Dirql);
324
325 INFO_(I8042PRT, "MouseInterrupt.Vector %lu\n",
326 PortDeviceExtension->MouseInterrupt.Vector);
327 INFO_(I8042PRT, "MouseInterrupt.Dirql %lu\n",
328 PortDeviceExtension->MouseInterrupt.Dirql);
329 INFO_(I8042PRT, "MouseInterrupt.DirqlMax %lu\n",
330 DirqlMax);
331 INFO_(I8042PRT, "MouseInterrupt.InterruptMode %s\n",
332 PortDeviceExtension->MouseInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched");
333 INFO_(I8042PRT, "MouseInterrupt.ShareInterrupt %s\n",
334 PortDeviceExtension->MouseInterrupt.ShareInterrupt ? "yes" : "no");
335 INFO_(I8042PRT, "MouseInterrupt.Affinity 0x%lx\n",
336 PortDeviceExtension->MouseInterrupt.Affinity);
337 Status = IoConnectInterrupt(
338 &PortDeviceExtension->MouseInterrupt.Object,
339 i8042MouInterruptService,
340 DeviceExtension, &PortDeviceExtension->SpinLock,
341 PortDeviceExtension->MouseInterrupt.Vector, PortDeviceExtension->MouseInterrupt.Dirql, DirqlMax,
342 PortDeviceExtension->MouseInterrupt.InterruptMode, PortDeviceExtension->MouseInterrupt.ShareInterrupt,
343 PortDeviceExtension->MouseInterrupt.Affinity, FALSE);
344 if (!NT_SUCCESS(Status))
345 {
346 WARN_(I8042PRT, "IoConnectInterrupt() failed with status 0x%08x\n", Status);
347 goto cleanup;
348 }
349
350 if (DirqlMax == PortDeviceExtension->MouseInterrupt.Dirql)
351 PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->MouseInterrupt.Object;
352
353 PortDeviceExtension->Flags |= MOUSE_INITIALIZED;
354 Status = STATUS_SUCCESS;
355
356 cleanup:
357 if (!NT_SUCCESS(Status))
358 {
359 PortDeviceExtension->Flags &= ~MOUSE_INITIALIZED;
360 if (PortDeviceExtension->MouseInterrupt.Object)
361 {
362 IoDisconnectInterrupt(PortDeviceExtension->MouseInterrupt.Object);
363 PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object;
364 }
365 }
366 return Status;
367 }
368
369 static NTSTATUS
370 EnableInterrupts(
371 IN PPORT_DEVICE_EXTENSION DeviceExtension,
372 IN UCHAR FlagsToDisable,
373 IN UCHAR FlagsToEnable)
374 {
375 i8042Flush(DeviceExtension);
376
377 if (!i8042ChangeMode(DeviceExtension, FlagsToDisable, FlagsToEnable))
378 return STATUS_UNSUCCESSFUL;
379
380 /* Reset the mouse (if any) to start the detection */
381 if (DeviceExtension->Flags & MOUSE_PRESENT)
382 {
383 KIRQL Irql;
384
385 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt);
386
387 i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_WRITE_MOUSE);
388 i8042Write(DeviceExtension, DeviceExtension->DataPort, MOU_CMD_RESET);
389
390 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql);
391 }
392
393 return STATUS_SUCCESS;
394 }
395
396 static NTSTATUS
397 StartProcedure(
398 IN PPORT_DEVICE_EXTENSION DeviceExtension)
399 {
400 NTSTATUS Status;
401 UCHAR FlagsToDisable = 0;
402 UCHAR FlagsToEnable = 0;
403
404 if (DeviceExtension->DataPort == 0)
405 {
406 /* Unable to do something at the moment */
407 return STATUS_SUCCESS;
408 }
409
410 if (!(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_PRESENT)))
411 {
412 /* Try to detect them */
413 TRACE_(I8042PRT, "Check if the controller is really a i8042\n");
414 Status = i8042BasicDetect(DeviceExtension);
415 if (!NT_SUCCESS(Status))
416 {
417 WARN_(I8042PRT, "i8042BasicDetect() failed with status 0x%08lx\n", Status);
418 return STATUS_UNSUCCESSFUL;
419 }
420 TRACE_(I8042PRT, "Detecting keyboard\n");
421 i8042DetectKeyboard(DeviceExtension);
422
423 TRACE_(I8042PRT, "Detecting mouse\n");
424 i8042DetectMouse(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
430 /* Connect interrupts */
431 if (DeviceExtension->Flags & KEYBOARD_PRESENT &&
432 DeviceExtension->Flags & KEYBOARD_CONNECTED &&
433 DeviceExtension->Flags & KEYBOARD_STARTED &&
434 !(DeviceExtension->Flags & KEYBOARD_INITIALIZED))
435 {
436 /* Keyboard is ready to be initialized */
437 Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension);
438 if (NT_SUCCESS(Status))
439 {
440 DeviceExtension->Flags |= KEYBOARD_INITIALIZED;
441 FlagsToDisable |= CCB_KBD_DISAB;
442 FlagsToEnable |= CCB_KBD_INT_ENAB;
443 }
444 }
445
446 if (DeviceExtension->Flags & MOUSE_PRESENT &&
447 DeviceExtension->Flags & MOUSE_CONNECTED &&
448 DeviceExtension->Flags & MOUSE_STARTED &&
449 !(DeviceExtension->Flags & MOUSE_INITIALIZED))
450 {
451 /* Mouse is ready to be initialized */
452 Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension);
453 if (NT_SUCCESS(Status))
454 {
455 DeviceExtension->Flags |= MOUSE_INITIALIZED;
456 FlagsToDisable |= CCB_MOUSE_DISAB;
457 FlagsToEnable |= CCB_MOUSE_INT_ENAB;
458 }
459 }
460
461 if (FlagsToEnable)
462 Status = EnableInterrupts(DeviceExtension, FlagsToDisable, FlagsToEnable);
463 else
464 Status = STATUS_SUCCESS;
465
466 return Status;
467 }
468
469 static NTSTATUS
470 i8042PnpStartDevice(
471 IN PDEVICE_OBJECT DeviceObject,
472 IN PCM_RESOURCE_LIST AllocatedResources,
473 IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
474 {
475 PFDO_DEVICE_EXTENSION DeviceExtension;
476 PPORT_DEVICE_EXTENSION PortDeviceExtension;
477 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated;
478 INTERRUPT_DATA InterruptData;
479 BOOLEAN FoundDataPort = FALSE;
480 BOOLEAN FoundControlPort = FALSE;
481 BOOLEAN FoundIrq = FALSE;
482 ULONG i;
483 NTSTATUS Status;
484
485 TRACE_(I8042PRT, "i8042PnpStartDevice(%p)\n", DeviceObject);
486 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
487 PortDeviceExtension = DeviceExtension->PortDeviceExtension;
488
489 ASSERT(DeviceExtension->PnpState == dsStopped);
490
491 if (!AllocatedResources)
492 {
493 WARN_(I8042PRT, "No allocated resources sent to driver\n");
494 return STATUS_INSUFFICIENT_RESOURCES;
495 }
496 if (AllocatedResources->Count != 1)
497 {
498 WARN_(I8042PRT, "Wrong number of allocated resources sent to driver\n");
499 return STATUS_INSUFFICIENT_RESOURCES;
500 }
501 if (AllocatedResources->List[0].PartialResourceList.Version != 1
502 || AllocatedResources->List[0].PartialResourceList.Revision != 1
503 || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1
504 || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1)
505 {
506 WARN_(I8042PRT, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
507 AllocatedResources->List[0].PartialResourceList.Version,
508 AllocatedResources->List[0].PartialResourceList.Revision,
509 AllocatedResourcesTranslated->List[0].PartialResourceList.Version,
510 AllocatedResourcesTranslated->List[0].PartialResourceList.Revision);
511 return STATUS_REVISION_MISMATCH;
512 }
513
514 /* Get Irq and optionally control port and data port */
515 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
516 {
517 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
518 ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];
519 switch (ResourceDescriptor->Type)
520 {
521 case CmResourceTypePort:
522 {
523 if (ResourceDescriptor->u.Port.Length == 1)
524 {
525 /* We assume that the first ressource will
526 * be the control port and the second one
527 * will be the data port...
528 */
529 if (!FoundDataPort)
530 {
531 PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
532 INFO_(I8042PRT, "Found data port: %p\n", PortDeviceExtension->DataPort);
533 FoundDataPort = TRUE;
534 }
535 else if (!FoundControlPort)
536 {
537 PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
538 INFO_(I8042PRT, "Found control port: %p\n", PortDeviceExtension->ControlPort);
539 FoundControlPort = TRUE;
540 }
541 else
542 {
543 WARN_(I8042PRT, "Too much I/O ranges provided: 0x%lx\n", ResourceDescriptor->u.Port.Length);
544 return STATUS_INVALID_PARAMETER;
545 }
546 }
547 else
548 WARN_(I8042PRT, "Invalid I/O range length: 0x%lx\n", ResourceDescriptor->u.Port.Length);
549 break;
550 }
551 case CmResourceTypeInterrupt:
552 {
553 if (FoundIrq)
554 return STATUS_INVALID_PARAMETER;
555 InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level;
556 InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector;
557 InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity;
558 if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
559 InterruptData.InterruptMode = Latched;
560 else
561 InterruptData.InterruptMode = LevelSensitive;
562 InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared);
563 INFO_(I8042PRT, "Found irq resource: %lu\n", ResourceDescriptor->u.Interrupt.Level);
564 FoundIrq = TRUE;
565 break;
566 }
567 default:
568 WARN_(I8042PRT, "Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
569 }
570 }
571
572 if (!FoundIrq)
573 {
574 WARN_(I8042PRT, "Interrupt resource was not found in allocated resources list\n");
575 return STATUS_INSUFFICIENT_RESOURCES;
576 }
577 else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort))
578 {
579 WARN_(I8042PRT, "Some required resources were not found in allocated resources list\n");
580 return STATUS_INSUFFICIENT_RESOURCES;
581 }
582 else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort))
583 {
584 WARN_(I8042PRT, "Too much resources were provided in allocated resources list\n");
585 return STATUS_INVALID_PARAMETER;
586 }
587
588 switch (DeviceExtension->Type)
589 {
590 case Keyboard:
591 {
592 RtlCopyMemory(
593 &PortDeviceExtension->KeyboardInterrupt,
594 &InterruptData,
595 sizeof(INTERRUPT_DATA));
596 PortDeviceExtension->Flags |= KEYBOARD_STARTED;
597 Status = StartProcedure(PortDeviceExtension);
598 break;
599 }
600 case Mouse:
601 {
602 RtlCopyMemory(
603 &PortDeviceExtension->MouseInterrupt,
604 &InterruptData,
605 sizeof(INTERRUPT_DATA));
606 PortDeviceExtension->Flags |= MOUSE_STARTED;
607 Status = StartProcedure(PortDeviceExtension);
608 break;
609 }
610 default:
611 {
612 ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type);
613 ASSERT(FALSE);
614 Status = STATUS_INVALID_DEVICE_REQUEST;
615 }
616 }
617
618 if (NT_SUCCESS(Status))
619 DeviceExtension->PnpState = dsStarted;
620
621 return Status;
622 }
623
624 NTSTATUS NTAPI
625 i8042Pnp(
626 IN PDEVICE_OBJECT DeviceObject,
627 IN PIRP Irp)
628 {
629 PIO_STACK_LOCATION Stack;
630 ULONG MinorFunction;
631 I8042_DEVICE_TYPE DeviceType;
632 ULONG_PTR Information = 0;
633 NTSTATUS Status;
634
635 Stack = IoGetCurrentIrpStackLocation(Irp);
636 MinorFunction = Stack->MinorFunction;
637 DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type;
638
639 switch (MinorFunction)
640 {
641 case IRP_MN_START_DEVICE: /* 0x00 */
642 {
643 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
644
645 /* Call lower driver (if any) */
646 if (DeviceType != PhysicalDeviceObject)
647 {
648 Status = ForwardIrpAndWait(DeviceObject, Irp);
649 if (NT_SUCCESS(Status))
650 Status = i8042PnpStartDevice(
651 DeviceObject,
652 Stack->Parameters.StartDevice.AllocatedResources,
653 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
654 }
655 else
656 Status = STATUS_SUCCESS;
657 break;
658 }
659 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */
660 {
661 switch (Stack->Parameters.QueryDeviceRelations.Type)
662 {
663 case BusRelations:
664 {
665 PDEVICE_RELATIONS DeviceRelations;
666
667 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
668 DeviceRelations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), I8042PRT_TAG);
669 if (DeviceRelations)
670 {
671 DeviceRelations->Count = 0;
672 Information = (ULONG_PTR)DeviceRelations;
673 Status = STATUS_SUCCESS;
674 }
675 else
676 Status = STATUS_INSUFFICIENT_RESOURCES;
677 break;
678 }
679 case RemovalRelations:
680 {
681 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
682 return ForwardIrpAndForget(DeviceObject, Irp);
683 }
684 default:
685 ERR_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
686 Stack->Parameters.QueryDeviceRelations.Type);
687 ASSERT(FALSE);
688 return ForwardIrpAndForget(DeviceObject, Irp);
689 }
690 break;
691 }
692 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */
693 {
694 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
695 /* Nothing to do */
696 Status = Irp->IoStatus.Status;
697 break;
698 }
699 default:
700 {
701 ERR_(I8042PRT, "IRP_MJ_PNP / unknown minor function 0x%x\n", MinorFunction);
702 ASSERT(FALSE);
703 return ForwardIrpAndForget(DeviceObject, Irp);
704 }
705 }
706
707 Irp->IoStatus.Information = Information;
708 Irp->IoStatus.Status = Status;
709 IoCompleteRequest(Irp, IO_NO_INCREMENT);
710 return Status;
711 }