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