- Remove much deprecated code for the mouse initialization (had been imported from...
[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(!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 TRACE_(I8042PRT, "Detecting keyboard\n");
417 i8042DetectKeyboard(DeviceExtension);
418
419 TRACE_(I8042PRT, "Detecting mouse\n");
420 i8042DetectMouse(DeviceExtension);
421
422 INFO_(I8042PRT, "Keyboard present: %s\n", DeviceExtension->Flags & KEYBOARD_PRESENT ? "YES" : "NO");
423 INFO_(I8042PRT, "Mouse present : %s\n", DeviceExtension->Flags & MOUSE_PRESENT ? "YES" : "NO");
424 }
425
426 /* Connect interrupts */
427 if (DeviceExtension->Flags & KEYBOARD_PRESENT &&
428 DeviceExtension->Flags & KEYBOARD_CONNECTED &&
429 DeviceExtension->Flags & KEYBOARD_STARTED &&
430 !(DeviceExtension->Flags & KEYBOARD_INITIALIZED))
431 {
432 /* Keyboard is ready to be initialized */
433 Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension);
434 if (NT_SUCCESS(Status))
435 {
436 DeviceExtension->Flags |= KEYBOARD_INITIALIZED;
437 FlagsToDisable |= CCB_KBD_DISAB;
438 FlagsToEnable |= CCB_KBD_INT_ENAB;
439 }
440 }
441
442 if (DeviceExtension->Flags & MOUSE_PRESENT &&
443 DeviceExtension->Flags & MOUSE_CONNECTED &&
444 DeviceExtension->Flags & MOUSE_STARTED &&
445 !(DeviceExtension->Flags & MOUSE_INITIALIZED))
446 {
447 /* Mouse is ready to be initialized */
448 Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension);
449 if (NT_SUCCESS(Status))
450 {
451 DeviceExtension->Flags |= MOUSE_INITIALIZED;
452 FlagsToDisable |= CCB_MOUSE_DISAB;
453 FlagsToEnable |= CCB_MOUSE_INT_ENAB;
454 }
455 }
456
457 if (FlagsToEnable)
458 Status = EnableInterrupts(DeviceExtension, FlagsToDisable, FlagsToEnable);
459 else
460 Status = STATUS_SUCCESS;
461
462 return Status;
463 }
464
465 static NTSTATUS
466 i8042PnpStartDevice(
467 IN PDEVICE_OBJECT DeviceObject,
468 IN PCM_RESOURCE_LIST AllocatedResources,
469 IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
470 {
471 PFDO_DEVICE_EXTENSION DeviceExtension;
472 PPORT_DEVICE_EXTENSION PortDeviceExtension;
473 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated;
474 INTERRUPT_DATA InterruptData;
475 BOOLEAN FoundDataPort = FALSE;
476 BOOLEAN FoundControlPort = FALSE;
477 BOOLEAN FoundIrq = FALSE;
478 ULONG i;
479 NTSTATUS Status;
480
481 TRACE_(I8042PRT, "i8042PnpStartDevice(%p)\n", DeviceObject);
482 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
483 PortDeviceExtension = DeviceExtension->PortDeviceExtension;
484
485 ASSERT(DeviceExtension->PnpState == dsStopped);
486
487 if (!AllocatedResources)
488 {
489 WARN_(I8042PRT, "No allocated resources sent to driver\n");
490 return STATUS_INSUFFICIENT_RESOURCES;
491 }
492 if (AllocatedResources->Count != 1)
493 {
494 WARN_(I8042PRT, "Wrong number of allocated resources sent to driver\n");
495 return STATUS_INSUFFICIENT_RESOURCES;
496 }
497 if (AllocatedResources->List[0].PartialResourceList.Version != 1
498 || AllocatedResources->List[0].PartialResourceList.Revision != 1
499 || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1
500 || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1)
501 {
502 WARN_(I8042PRT, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
503 AllocatedResources->List[0].PartialResourceList.Version,
504 AllocatedResources->List[0].PartialResourceList.Revision,
505 AllocatedResourcesTranslated->List[0].PartialResourceList.Version,
506 AllocatedResourcesTranslated->List[0].PartialResourceList.Revision);
507 return STATUS_REVISION_MISMATCH;
508 }
509
510 /* Get Irq and optionally control port and data port */
511 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
512 {
513 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
514 ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];
515 switch (ResourceDescriptor->Type)
516 {
517 case CmResourceTypePort:
518 {
519 if (ResourceDescriptor->u.Port.Length == 1)
520 {
521 /* We assume that the first ressource will
522 * be the control port and the second one
523 * will be the data port...
524 */
525 if (!FoundDataPort)
526 {
527 PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
528 INFO_(I8042PRT, "Found data port: %p\n", PortDeviceExtension->DataPort);
529 FoundDataPort = TRUE;
530 }
531 else if (!FoundControlPort)
532 {
533 PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart);
534 INFO_(I8042PRT, "Found control port: %p\n", PortDeviceExtension->ControlPort);
535 FoundControlPort = TRUE;
536 }
537 else
538 {
539 WARN_(I8042PRT, "Too much I/O ranges provided: 0x%lx\n", ResourceDescriptor->u.Port.Length);
540 return STATUS_INVALID_PARAMETER;
541 }
542 }
543 else
544 WARN_(I8042PRT, "Invalid I/O range length: 0x%lx\n", ResourceDescriptor->u.Port.Length);
545 break;
546 }
547 case CmResourceTypeInterrupt:
548 {
549 if (FoundIrq)
550 return STATUS_INVALID_PARAMETER;
551 InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level;
552 InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector;
553 InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity;
554 if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
555 InterruptData.InterruptMode = Latched;
556 else
557 InterruptData.InterruptMode = LevelSensitive;
558 InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared);
559 INFO_(I8042PRT, "Found irq resource: %lu\n", ResourceDescriptor->u.Interrupt.Level);
560 FoundIrq = TRUE;
561 break;
562 }
563 default:
564 WARN_(I8042PRT, "Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
565 }
566 }
567
568 if (!FoundIrq)
569 {
570 WARN_(I8042PRT, "Interrupt resource was not found in allocated resources list\n");
571 return STATUS_INSUFFICIENT_RESOURCES;
572 }
573 else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort))
574 {
575 WARN_(I8042PRT, "Some required resources were not found in allocated resources list\n");
576 return STATUS_INSUFFICIENT_RESOURCES;
577 }
578 else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort))
579 {
580 WARN_(I8042PRT, "Too much resources were provided in allocated resources list\n");
581 return STATUS_INVALID_PARAMETER;
582 }
583
584 switch (DeviceExtension->Type)
585 {
586 case Keyboard:
587 {
588 RtlCopyMemory(
589 &PortDeviceExtension->KeyboardInterrupt,
590 &InterruptData,
591 sizeof(INTERRUPT_DATA));
592 PortDeviceExtension->Flags |= KEYBOARD_STARTED;
593 Status = StartProcedure(PortDeviceExtension);
594 break;
595 }
596 case Mouse:
597 {
598 RtlCopyMemory(
599 &PortDeviceExtension->MouseInterrupt,
600 &InterruptData,
601 sizeof(INTERRUPT_DATA));
602 PortDeviceExtension->Flags |= MOUSE_STARTED;
603 Status = StartProcedure(PortDeviceExtension);
604 break;
605 }
606 default:
607 {
608 ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type);
609 ASSERT(FALSE);
610 Status = STATUS_INVALID_DEVICE_REQUEST;
611 }
612 }
613
614 if (NT_SUCCESS(Status))
615 DeviceExtension->PnpState = dsStarted;
616
617 return Status;
618 }
619
620 NTSTATUS NTAPI
621 i8042Pnp(
622 IN PDEVICE_OBJECT DeviceObject,
623 IN PIRP Irp)
624 {
625 PIO_STACK_LOCATION Stack;
626 ULONG MinorFunction;
627 I8042_DEVICE_TYPE DeviceType;
628 ULONG_PTR Information = 0;
629 NTSTATUS Status;
630
631 Stack = IoGetCurrentIrpStackLocation(Irp);
632 MinorFunction = Stack->MinorFunction;
633 DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type;
634
635 switch (MinorFunction)
636 {
637 case IRP_MN_START_DEVICE: /* 0x00 */
638 {
639 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
640
641 /* Call lower driver (if any) */
642 if (DeviceType != PhysicalDeviceObject)
643 {
644 Status = ForwardIrpAndWait(DeviceObject, Irp);
645 if (NT_SUCCESS(Status))
646 Status = i8042PnpStartDevice(
647 DeviceObject,
648 Stack->Parameters.StartDevice.AllocatedResources,
649 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
650 }
651 else
652 Status = STATUS_SUCCESS;
653 break;
654 }
655 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */
656 {
657 switch (Stack->Parameters.QueryDeviceRelations.Type)
658 {
659 case BusRelations:
660 {
661 PDEVICE_RELATIONS DeviceRelations;
662
663 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
664 DeviceRelations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), I8042PRT_TAG);
665 if (DeviceRelations)
666 {
667 DeviceRelations->Count = 0;
668 Information = (ULONG_PTR)DeviceRelations;
669 Status = STATUS_SUCCESS;
670 }
671 else
672 Status = STATUS_INSUFFICIENT_RESOURCES;
673 break;
674 }
675 case RemovalRelations:
676 {
677 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
678 return ForwardIrpAndForget(DeviceObject, Irp);
679 }
680 default:
681 ERR_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
682 Stack->Parameters.QueryDeviceRelations.Type);
683 ASSERT(FALSE);
684 return ForwardIrpAndForget(DeviceObject, Irp);
685 }
686 break;
687 }
688 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */
689 {
690 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
691 /* Nothing to do */
692 Status = Irp->IoStatus.Status;
693 break;
694 }
695 default:
696 {
697 ERR_(I8042PRT, "IRP_MJ_PNP / unknown minor function 0x%x\n", MinorFunction);
698 ASSERT(FALSE);
699 return ForwardIrpAndForget(DeviceObject, Irp);
700 }
701 }
702
703 Irp->IoStatus.Information = Information;
704 Irp->IoStatus.Status = Status;
705 IoCompleteRequest(Irp, IO_NO_INCREMENT);
706 return Status;
707 }