[USB]
[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 return STATUS_SUCCESS;
385 }
386
387 static NTSTATUS
388 StartProcedure(
389 IN PPORT_DEVICE_EXTENSION DeviceExtension)
390 {
391 NTSTATUS Status;
392 UCHAR FlagsToDisable = 0;
393 UCHAR FlagsToEnable = 0;
394 KIRQL Irql;
395
396 if (DeviceExtension->DataPort == 0)
397 {
398 /* Unable to do something at the moment */
399 return STATUS_SUCCESS;
400 }
401
402 if (!(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_PRESENT)))
403 {
404 /* Try to detect them */
405 TRACE_(I8042PRT, "Check if the controller is really a i8042\n");
406 Status = i8042BasicDetect(DeviceExtension);
407 if (!NT_SUCCESS(Status))
408 {
409 WARN_(I8042PRT, "i8042BasicDetect() failed with status 0x%08lx\n", Status);
410 return STATUS_UNSUCCESSFUL;
411 }
412
413 /* First detect the mouse and then the keyboard!
414 If we do it the other way round, some systems throw away settings like the keyboard translation, when detecting the mouse.
415
416 Don't detect the mouse if we're in 1st stage setup! */
417 if(!IsFirstStageSetup())
418 {
419 TRACE_(I8042PRT, "Detecting mouse\n");
420 i8042DetectMouse(DeviceExtension);
421 }
422
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 DeviceExtension->Flags &= ~(KEYBOARD_PRESENT | MOUSE_PRESENT);
445 return Status;
446 }
447 }
448
449 /* Connect interrupts */
450 if (DeviceExtension->Flags & KEYBOARD_PRESENT &&
451 DeviceExtension->Flags & KEYBOARD_CONNECTED &&
452 DeviceExtension->Flags & KEYBOARD_STARTED &&
453 !(DeviceExtension->Flags & KEYBOARD_INITIALIZED))
454 {
455 /* Keyboard is ready to be initialized */
456 Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension);
457 if (NT_SUCCESS(Status))
458 {
459 DeviceExtension->Flags |= KEYBOARD_INITIALIZED;
460 }
461 }
462
463 if (DeviceExtension->Flags & MOUSE_PRESENT &&
464 DeviceExtension->Flags & MOUSE_CONNECTED &&
465 DeviceExtension->Flags & MOUSE_STARTED &&
466 !(DeviceExtension->Flags & MOUSE_INITIALIZED))
467 {
468 /* Mouse is ready to be initialized */
469 Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension);
470 if (NT_SUCCESS(Status))
471 {
472 DeviceExtension->Flags |= MOUSE_INITIALIZED;
473 }
474
475 /* Start the mouse */
476 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt);
477 i8042IsrWritePort(DeviceExtension, MOU_CMD_RESET, CTRL_WRITE_MOUSE);
478 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql);
479 }
480
481 return Status;
482 }
483
484 static NTSTATUS
485 i8042PnpStartDevice(
486 IN PDEVICE_OBJECT DeviceObject,
487 IN PCM_RESOURCE_LIST AllocatedResources,
488 IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
489 {
490 PFDO_DEVICE_EXTENSION DeviceExtension;
491 PPORT_DEVICE_EXTENSION PortDeviceExtension;
492 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated;
493 INTERRUPT_DATA InterruptData;
494 BOOLEAN FoundDataPort = FALSE;
495 BOOLEAN FoundControlPort = FALSE;
496 BOOLEAN FoundIrq = FALSE;
497 ULONG i;
498 NTSTATUS Status;
499
500 TRACE_(I8042PRT, "i8042PnpStartDevice(%p)\n", DeviceObject);
501 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
502 PortDeviceExtension = DeviceExtension->PortDeviceExtension;
503
504 ASSERT(DeviceExtension->PnpState == dsStopped);
505
506 if (!AllocatedResources)
507 {
508 WARN_(I8042PRT, "No allocated resources sent to driver\n");
509 return STATUS_INSUFFICIENT_RESOURCES;
510 }
511 if (AllocatedResources->Count != 1)
512 {
513 WARN_(I8042PRT, "Wrong number of allocated resources sent to driver\n");
514 return STATUS_INSUFFICIENT_RESOURCES;
515 }
516 if (AllocatedResources->List[0].PartialResourceList.Version != 1
517 || AllocatedResources->List[0].PartialResourceList.Revision != 1
518 || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1
519 || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1)
520 {
521 WARN_(I8042PRT, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
522 AllocatedResources->List[0].PartialResourceList.Version,
523 AllocatedResources->List[0].PartialResourceList.Revision,
524 AllocatedResourcesTranslated->List[0].PartialResourceList.Version,
525 AllocatedResourcesTranslated->List[0].PartialResourceList.Revision);
526 return STATUS_REVISION_MISMATCH;
527 }
528
529 /* Get Irq and optionally control port and data port */
530 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
531 {
532 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
533 ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];
534 switch (ResourceDescriptor->Type)
535 {
536 case CmResourceTypePort:
537 {
538 if (ResourceDescriptor->u.Port.Length == 1)
539 {
540 /* We assume that the first ressource will
541 * be the control port and the second one
542 * will be the data port...
543 */
544 if (!FoundDataPort)
545 {
546 PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
547 INFO_(I8042PRT, "Found data port: %p\n", PortDeviceExtension->DataPort);
548 FoundDataPort = TRUE;
549 }
550 else if (!FoundControlPort)
551 {
552 PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
553 INFO_(I8042PRT, "Found control port: %p\n", PortDeviceExtension->ControlPort);
554 FoundControlPort = TRUE;
555 }
556 else
557 {
558 WARN_(I8042PRT, "Too much I/O ranges provided: 0x%lx\n", ResourceDescriptor->u.Port.Length);
559 return STATUS_INVALID_PARAMETER;
560 }
561 }
562 else
563 WARN_(I8042PRT, "Invalid I/O range length: 0x%lx\n", ResourceDescriptor->u.Port.Length);
564 break;
565 }
566 case CmResourceTypeInterrupt:
567 {
568 if (FoundIrq)
569 return STATUS_INVALID_PARAMETER;
570 InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level;
571 InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector;
572 InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity;
573 if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
574 InterruptData.InterruptMode = Latched;
575 else
576 InterruptData.InterruptMode = LevelSensitive;
577 InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared);
578 INFO_(I8042PRT, "Found irq resource: %lu\n", ResourceDescriptor->u.Interrupt.Level);
579 FoundIrq = TRUE;
580 break;
581 }
582 default:
583 WARN_(I8042PRT, "Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
584 }
585 }
586
587 if (!FoundIrq)
588 {
589 WARN_(I8042PRT, "Interrupt resource was not found in allocated resources list\n");
590 return STATUS_INSUFFICIENT_RESOURCES;
591 }
592 else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort))
593 {
594 WARN_(I8042PRT, "Some required resources were not found in allocated resources list\n");
595 return STATUS_INSUFFICIENT_RESOURCES;
596 }
597 else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort))
598 {
599 WARN_(I8042PRT, "Too much resources were provided in allocated resources list\n");
600 return STATUS_INVALID_PARAMETER;
601 }
602
603 switch (DeviceExtension->Type)
604 {
605 case Keyboard:
606 {
607 RtlCopyMemory(
608 &PortDeviceExtension->KeyboardInterrupt,
609 &InterruptData,
610 sizeof(INTERRUPT_DATA));
611 PortDeviceExtension->Flags |= KEYBOARD_STARTED;
612 Status = StartProcedure(PortDeviceExtension);
613 break;
614 }
615 case Mouse:
616 {
617 RtlCopyMemory(
618 &PortDeviceExtension->MouseInterrupt,
619 &InterruptData,
620 sizeof(INTERRUPT_DATA));
621 PortDeviceExtension->Flags |= MOUSE_STARTED;
622 Status = StartProcedure(PortDeviceExtension);
623 break;
624 }
625 default:
626 {
627 WARN_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type);
628 ASSERT(!(PortDeviceExtension->Flags & KEYBOARD_CONNECTED) || !(PortDeviceExtension->Flags & MOUSE_CONNECTED));
629 Status = STATUS_INVALID_DEVICE_REQUEST;
630 }
631 }
632
633 if (NT_SUCCESS(Status))
634 DeviceExtension->PnpState = dsStarted;
635
636 return Status;
637 }
638
639 NTSTATUS NTAPI
640 i8042Pnp(
641 IN PDEVICE_OBJECT DeviceObject,
642 IN PIRP Irp)
643 {
644 PIO_STACK_LOCATION Stack;
645 ULONG MinorFunction;
646 I8042_DEVICE_TYPE DeviceType;
647 ULONG_PTR Information = 0;
648 NTSTATUS Status;
649
650 Stack = IoGetCurrentIrpStackLocation(Irp);
651 MinorFunction = Stack->MinorFunction;
652 DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type;
653
654 switch (MinorFunction)
655 {
656 case IRP_MN_START_DEVICE: /* 0x00 */
657 {
658 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
659
660 /* Call lower driver (if any) */
661 if (DeviceType != PhysicalDeviceObject)
662 {
663 Status = ForwardIrpAndWait(DeviceObject, Irp);
664 if (NT_SUCCESS(Status))
665 Status = i8042PnpStartDevice(
666 DeviceObject,
667 Stack->Parameters.StartDevice.AllocatedResources,
668 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
669 }
670 else
671 Status = STATUS_SUCCESS;
672 break;
673 }
674 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */
675 {
676 switch (Stack->Parameters.QueryDeviceRelations.Type)
677 {
678 case BusRelations:
679 {
680 PDEVICE_RELATIONS DeviceRelations;
681
682 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
683 DeviceRelations = ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
684 if (DeviceRelations)
685 {
686 DeviceRelations->Count = 0;
687 Information = (ULONG_PTR)DeviceRelations;
688 Status = STATUS_SUCCESS;
689 }
690 else
691 Status = STATUS_INSUFFICIENT_RESOURCES;
692 break;
693 }
694 case RemovalRelations:
695 {
696 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
697 return ForwardIrpAndForget(DeviceObject, Irp);
698 }
699 default:
700 ERR_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
701 Stack->Parameters.QueryDeviceRelations.Type);
702 ASSERT(FALSE);
703 return ForwardIrpAndForget(DeviceObject, Irp);
704 }
705 break;
706 }
707 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */
708 {
709 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
710 /* Nothing to do */
711 Status = Irp->IoStatus.Status;
712 break;
713 }
714 case IRP_MN_QUERY_PNP_DEVICE_STATE: /* 0x14 */
715 {
716 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n");
717 /* Nothing much to tell */
718 Information = 0;
719 Status = STATUS_SUCCESS;
720 break;
721 }
722 default:
723 {
724 ERR_(I8042PRT, "IRP_MJ_PNP / unknown minor function 0x%x\n", MinorFunction);
725 return ForwardIrpAndForget(DeviceObject, Irp);
726 }
727 }
728
729 Irp->IoStatus.Information = Information;
730 Irp->IoStatus.Status = Status;
731 IoCompleteRequest(Irp, IO_NO_INCREMENT);
732 return Status;
733 }