- Implement ProtocolResetComplete
[reactos.git] / 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(!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 }