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