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 278 // Enable keyboard clock line 279 i8042Write(PortDeviceExtension, PortDeviceExtension->ControlPort, KBD_CLK_ENABLE); 280 281 DirqlMax = MAX( 282 PortDeviceExtension->KeyboardInterrupt.Dirql, 283 PortDeviceExtension->MouseInterrupt.Dirql); 284 285 INFO_(I8042PRT, "KeyboardInterrupt.Vector %lu\n", 286 PortDeviceExtension->KeyboardInterrupt.Vector); 287 INFO_(I8042PRT, "KeyboardInterrupt.Dirql %lu\n", 288 PortDeviceExtension->KeyboardInterrupt.Dirql); 289 INFO_(I8042PRT, "KeyboardInterrupt.DirqlMax %lu\n", 290 DirqlMax); 291 INFO_(I8042PRT, "KeyboardInterrupt.InterruptMode %s\n", 292 PortDeviceExtension->KeyboardInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched"); 293 INFO_(I8042PRT, "KeyboardInterrupt.ShareInterrupt %s\n", 294 PortDeviceExtension->KeyboardInterrupt.ShareInterrupt ? "yes" : "no"); 295 INFO_(I8042PRT, "KeyboardInterrupt.Affinity 0x%lx\n", 296 PortDeviceExtension->KeyboardInterrupt.Affinity); 297 Status = IoConnectInterrupt( 298 &PortDeviceExtension->KeyboardInterrupt.Object, 299 i8042KbdInterruptService, 300 DeviceExtension, &PortDeviceExtension->SpinLock, 301 PortDeviceExtension->KeyboardInterrupt.Vector, PortDeviceExtension->KeyboardInterrupt.Dirql, DirqlMax, 302 PortDeviceExtension->KeyboardInterrupt.InterruptMode, PortDeviceExtension->KeyboardInterrupt.ShareInterrupt, 303 PortDeviceExtension->KeyboardInterrupt.Affinity, FALSE); 304 if (!NT_SUCCESS(Status)) 305 { 306 WARN_(I8042PRT, "IoConnectInterrupt() failed with status 0x%08x\n", Status); 307 return Status; 308 } 309 310 if (DirqlMax == PortDeviceExtension->KeyboardInterrupt.Dirql) 311 PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object; 312 PortDeviceExtension->Flags |= KEYBOARD_INITIALIZED; 313 return STATUS_SUCCESS; 314 } 315 316 static NTSTATUS 317 i8042ConnectMouseInterrupt( 318 IN PI8042_MOUSE_EXTENSION DeviceExtension) 319 { 320 PPORT_DEVICE_EXTENSION PortDeviceExtension; 321 KIRQL DirqlMax; 322 NTSTATUS Status; 323 324 TRACE_(I8042PRT, "i8042ConnectMouseInterrupt()\n"); 325 326 Status = i8042MouInitialize(DeviceExtension); 327 if (!NT_SUCCESS(Status)) 328 return Status; 329 330 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 331 DirqlMax = MAX( 332 PortDeviceExtension->KeyboardInterrupt.Dirql, 333 PortDeviceExtension->MouseInterrupt.Dirql); 334 335 INFO_(I8042PRT, "MouseInterrupt.Vector %lu\n", 336 PortDeviceExtension->MouseInterrupt.Vector); 337 INFO_(I8042PRT, "MouseInterrupt.Dirql %lu\n", 338 PortDeviceExtension->MouseInterrupt.Dirql); 339 INFO_(I8042PRT, "MouseInterrupt.DirqlMax %lu\n", 340 DirqlMax); 341 INFO_(I8042PRT, "MouseInterrupt.InterruptMode %s\n", 342 PortDeviceExtension->MouseInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched"); 343 INFO_(I8042PRT, "MouseInterrupt.ShareInterrupt %s\n", 344 PortDeviceExtension->MouseInterrupt.ShareInterrupt ? "yes" : "no"); 345 INFO_(I8042PRT, "MouseInterrupt.Affinity 0x%lx\n", 346 PortDeviceExtension->MouseInterrupt.Affinity); 347 Status = IoConnectInterrupt( 348 &PortDeviceExtension->MouseInterrupt.Object, 349 i8042MouInterruptService, 350 DeviceExtension, &PortDeviceExtension->SpinLock, 351 PortDeviceExtension->MouseInterrupt.Vector, PortDeviceExtension->MouseInterrupt.Dirql, DirqlMax, 352 PortDeviceExtension->MouseInterrupt.InterruptMode, PortDeviceExtension->MouseInterrupt.ShareInterrupt, 353 PortDeviceExtension->MouseInterrupt.Affinity, FALSE); 354 if (!NT_SUCCESS(Status)) 355 { 356 WARN_(I8042PRT, "IoConnectInterrupt() failed with status 0x%08x\n", Status); 357 goto cleanup; 358 } 359 360 if (DirqlMax == PortDeviceExtension->MouseInterrupt.Dirql) 361 PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->MouseInterrupt.Object; 362 363 PortDeviceExtension->Flags |= MOUSE_INITIALIZED; 364 Status = STATUS_SUCCESS; 365 366 cleanup: 367 if (!NT_SUCCESS(Status)) 368 { 369 PortDeviceExtension->Flags &= ~MOUSE_INITIALIZED; 370 if (PortDeviceExtension->MouseInterrupt.Object) 371 { 372 IoDisconnectInterrupt(PortDeviceExtension->MouseInterrupt.Object); 373 PortDeviceExtension->HighestDIRQLInterrupt = PortDeviceExtension->KeyboardInterrupt.Object; 374 } 375 } 376 return Status; 377 } 378 379 static NTSTATUS 380 EnableInterrupts( 381 IN PPORT_DEVICE_EXTENSION DeviceExtension, 382 IN UCHAR FlagsToDisable, 383 IN UCHAR FlagsToEnable) 384 { 385 i8042Flush(DeviceExtension); 386 387 if (!i8042ChangeMode(DeviceExtension, FlagsToDisable, FlagsToEnable)) 388 return STATUS_UNSUCCESSFUL; 389 390 return STATUS_SUCCESS; 391 } 392 393 static NTSTATUS 394 StartProcedure( 395 IN PPORT_DEVICE_EXTENSION DeviceExtension) 396 { 397 NTSTATUS Status = STATUS_UNSUCCESSFUL; 398 UCHAR FlagsToDisable = 0; 399 UCHAR FlagsToEnable = 0; 400 KIRQL Irql; 401 402 if (DeviceExtension->DataPort == 0) 403 { 404 /* Unable to do something at the moment */ 405 return STATUS_SUCCESS; 406 } 407 408 if (!(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_PRESENT))) 409 { 410 /* Try to detect them */ 411 TRACE_(I8042PRT, "Check if the controller is really a i8042\n"); 412 Status = i8042BasicDetect(DeviceExtension); 413 if (!NT_SUCCESS(Status)) 414 { 415 WARN_(I8042PRT, "i8042BasicDetect() failed with status 0x%08lx\n", Status); 416 return STATUS_UNSUCCESSFUL; 417 } 418 419 /* First detect the mouse and then the keyboard! 420 If we do it the other way round, some systems throw away settings like the keyboard translation, when detecting the mouse. */ 421 TRACE_(I8042PRT, "Detecting mouse\n"); 422 i8042DetectMouse(DeviceExtension); 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 WARN_(I8042PRT, "EnableInterrupts failed: %lx\n", Status); 445 DeviceExtension->Flags &= ~(KEYBOARD_PRESENT | MOUSE_PRESENT); 446 return Status; 447 } 448 } 449 450 /* Connect interrupts */ 451 if (DeviceExtension->Flags & KEYBOARD_PRESENT && 452 DeviceExtension->Flags & KEYBOARD_CONNECTED && 453 DeviceExtension->Flags & KEYBOARD_STARTED && 454 !(DeviceExtension->Flags & KEYBOARD_INITIALIZED)) 455 { 456 /* Keyboard is ready to be initialized */ 457 Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension); 458 if (NT_SUCCESS(Status)) 459 { 460 DeviceExtension->Flags |= KEYBOARD_INITIALIZED; 461 } 462 else 463 { 464 WARN_(I8042PRT, "i8042ConnectKeyboardInterrupt failed: %lx\n", Status); 465 } 466 } 467 468 if (DeviceExtension->Flags & MOUSE_PRESENT && 469 DeviceExtension->Flags & MOUSE_CONNECTED && 470 DeviceExtension->Flags & MOUSE_STARTED && 471 !(DeviceExtension->Flags & MOUSE_INITIALIZED)) 472 { 473 /* Mouse is ready to be initialized */ 474 Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension); 475 if (NT_SUCCESS(Status)) 476 { 477 DeviceExtension->Flags |= MOUSE_INITIALIZED; 478 } 479 else 480 { 481 WARN_(I8042PRT, "i8042ConnectMouseInterrupt failed: %lx\n", Status); 482 } 483 484 /* Start the mouse */ 485 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt); 486 /* HACK: the mouse has already been reset in i8042DetectMouse. This second 487 reset prevents some touchpads/mice from working (Dell D531, D600). 488 See CORE-6901 */ 489 if (!(i8042HwFlags & FL_INITHACK)) 490 { 491 i8042IsrWritePort(DeviceExtension, MOU_CMD_RESET, CTRL_WRITE_MOUSE); 492 } 493 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql); 494 } 495 496 return Status; 497 } 498 499 static NTSTATUS 500 i8042PnpStartDevice( 501 IN PDEVICE_OBJECT DeviceObject, 502 IN PCM_RESOURCE_LIST AllocatedResources, 503 IN PCM_RESOURCE_LIST AllocatedResourcesTranslated) 504 { 505 PFDO_DEVICE_EXTENSION DeviceExtension; 506 PPORT_DEVICE_EXTENSION PortDeviceExtension; 507 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated; 508 INTERRUPT_DATA InterruptData = { NULL }; 509 BOOLEAN FoundDataPort = FALSE; 510 BOOLEAN FoundControlPort = FALSE; 511 BOOLEAN FoundIrq = FALSE; 512 ULONG i; 513 NTSTATUS Status; 514 515 TRACE_(I8042PRT, "i8042PnpStartDevice(%p)\n", DeviceObject); 516 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 517 PortDeviceExtension = DeviceExtension->PortDeviceExtension; 518 519 ASSERT(DeviceExtension->PnpState == dsStopped); 520 521 if (!AllocatedResources) 522 { 523 WARN_(I8042PRT, "No allocated resources sent to driver\n"); 524 return STATUS_INSUFFICIENT_RESOURCES; 525 } 526 if (AllocatedResources->Count != 1) 527 { 528 WARN_(I8042PRT, "Wrong number of allocated resources sent to driver\n"); 529 return STATUS_INSUFFICIENT_RESOURCES; 530 } 531 if (AllocatedResources->List[0].PartialResourceList.Version != 1 532 || AllocatedResources->List[0].PartialResourceList.Revision != 1 533 || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1 534 || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1) 535 { 536 WARN_(I8042PRT, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n", 537 AllocatedResources->List[0].PartialResourceList.Version, 538 AllocatedResources->List[0].PartialResourceList.Revision, 539 AllocatedResourcesTranslated->List[0].PartialResourceList.Version, 540 AllocatedResourcesTranslated->List[0].PartialResourceList.Revision); 541 return STATUS_REVISION_MISMATCH; 542 } 543 544 /* Get Irq and optionally control port and data port */ 545 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++) 546 { 547 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i]; 548 ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i]; 549 switch (ResourceDescriptor->Type) 550 { 551 case CmResourceTypePort: 552 { 553 if (ResourceDescriptor->u.Port.Length == 1) 554 { 555 /* We assume that the first resource will 556 * be the control port and the second one 557 * will be the data port... 558 */ 559 if (!FoundDataPort) 560 { 561 PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart); 562 INFO_(I8042PRT, "Found data port: %p\n", PortDeviceExtension->DataPort); 563 FoundDataPort = TRUE; 564 } 565 else if (!FoundControlPort) 566 { 567 PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor->u.Port.Start.u.LowPart); 568 INFO_(I8042PRT, "Found control port: %p\n", PortDeviceExtension->ControlPort); 569 FoundControlPort = TRUE; 570 } 571 else 572 { 573 /* FIXME: implement PS/2 Active Multiplexing */ 574 ERR_(I8042PRT, "Unhandled I/O ranges provided: 0x%lx\n", ResourceDescriptor->u.Port.Length); 575 } 576 } 577 else 578 WARN_(I8042PRT, "Invalid I/O range length: 0x%lx\n", ResourceDescriptor->u.Port.Length); 579 break; 580 } 581 case CmResourceTypeInterrupt: 582 { 583 if (FoundIrq) 584 return STATUS_INVALID_PARAMETER; 585 InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level; 586 InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector; 587 InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity; 588 if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED) 589 InterruptData.InterruptMode = Latched; 590 else 591 InterruptData.InterruptMode = LevelSensitive; 592 InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared); 593 INFO_(I8042PRT, "Found irq resource: %lu\n", ResourceDescriptor->u.Interrupt.Level); 594 FoundIrq = TRUE; 595 break; 596 } 597 default: 598 WARN_(I8042PRT, "Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type); 599 } 600 } 601 602 if (!FoundIrq) 603 { 604 WARN_(I8042PRT, "Interrupt resource was not found in allocated resources list\n"); 605 return STATUS_INSUFFICIENT_RESOURCES; 606 } 607 else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort)) 608 { 609 WARN_(I8042PRT, "Some required resources were not found in allocated resources list\n"); 610 return STATUS_INSUFFICIENT_RESOURCES; 611 } 612 else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort)) 613 { 614 WARN_(I8042PRT, "Too much resources were provided in allocated resources list\n"); 615 return STATUS_INVALID_PARAMETER; 616 } 617 618 switch (DeviceExtension->Type) 619 { 620 case Keyboard: 621 { 622 RtlCopyMemory( 623 &PortDeviceExtension->KeyboardInterrupt, 624 &InterruptData, 625 sizeof(INTERRUPT_DATA)); 626 PortDeviceExtension->Flags |= KEYBOARD_STARTED; 627 Status = StartProcedure(PortDeviceExtension); 628 break; 629 } 630 case Mouse: 631 { 632 RtlCopyMemory( 633 &PortDeviceExtension->MouseInterrupt, 634 &InterruptData, 635 sizeof(INTERRUPT_DATA)); 636 PortDeviceExtension->Flags |= MOUSE_STARTED; 637 Status = StartProcedure(PortDeviceExtension); 638 break; 639 } 640 default: 641 { 642 WARN_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type); 643 ASSERT(!(PortDeviceExtension->Flags & KEYBOARD_CONNECTED) || !(PortDeviceExtension->Flags & MOUSE_CONNECTED)); 644 Status = STATUS_INVALID_DEVICE_REQUEST; 645 } 646 } 647 648 if (NT_SUCCESS(Status)) 649 DeviceExtension->PnpState = dsStarted; 650 651 return Status; 652 } 653 654 static VOID 655 i8042RemoveDevice( 656 IN PDEVICE_OBJECT DeviceObject) 657 { 658 PI8042_DRIVER_EXTENSION DriverExtension; 659 KIRQL OldIrql; 660 PFDO_DEVICE_EXTENSION DeviceExtension; 661 662 DriverExtension = (PI8042_DRIVER_EXTENSION)IoGetDriverObjectExtension(DeviceObject->DriverObject, DeviceObject->DriverObject); 663 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 664 665 KeAcquireSpinLock(&DriverExtension->DeviceListLock, &OldIrql); 666 RemoveEntryList(&DeviceExtension->ListEntry); 667 KeReleaseSpinLock(&DriverExtension->DeviceListLock, OldIrql); 668 669 IoDetachDevice(DeviceExtension->LowerDevice); 670 671 IoDeleteDevice(DeviceObject); 672 } 673 674 NTSTATUS NTAPI 675 i8042Pnp( 676 IN PDEVICE_OBJECT DeviceObject, 677 IN PIRP Irp) 678 { 679 PIO_STACK_LOCATION Stack; 680 ULONG MinorFunction; 681 I8042_DEVICE_TYPE DeviceType; 682 ULONG_PTR Information = 0; 683 NTSTATUS Status; 684 685 Stack = IoGetCurrentIrpStackLocation(Irp); 686 MinorFunction = Stack->MinorFunction; 687 DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type; 688 689 switch (MinorFunction) 690 { 691 case IRP_MN_START_DEVICE: /* 0x00 */ 692 { 693 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); 694 695 /* Call lower driver (if any) */ 696 if (DeviceType != PhysicalDeviceObject) 697 { 698 Status = ForwardIrpAndWait(DeviceObject, Irp); 699 if (NT_SUCCESS(Status)) 700 Status = i8042PnpStartDevice( 701 DeviceObject, 702 Stack->Parameters.StartDevice.AllocatedResources, 703 Stack->Parameters.StartDevice.AllocatedResourcesTranslated); 704 } 705 else 706 Status = STATUS_SUCCESS; 707 break; 708 } 709 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */ 710 { 711 switch (Stack->Parameters.QueryDeviceRelations.Type) 712 { 713 case BusRelations: 714 { 715 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); 716 return ForwardIrpAndForget(DeviceObject, Irp); 717 } 718 case RemovalRelations: 719 { 720 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); 721 return ForwardIrpAndForget(DeviceObject, Irp); 722 } 723 default: 724 ERR_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", 725 Stack->Parameters.QueryDeviceRelations.Type); 726 return ForwardIrpAndForget(DeviceObject, Irp); 727 } 728 break; 729 } 730 case IRP_MN_QUERY_CAPABILITIES: /* (optional) 0x09 */ 731 { 732 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n"); 733 return ForwardIrpAndForget(DeviceObject, Irp); 734 } 735 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */ 736 { 737 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n"); 738 return ForwardIrpAndForget(DeviceObject, Irp); 739 } 740 case IRP_MN_QUERY_PNP_DEVICE_STATE: /* (optional) 0x14 */ 741 { 742 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n"); 743 return ForwardIrpAndForget(DeviceObject, Irp); 744 } 745 case IRP_MN_QUERY_REMOVE_DEVICE: 746 { 747 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_REMOVE_DEVICE\n"); 748 return ForwardIrpAndForget(DeviceObject, Irp); 749 } 750 case IRP_MN_CANCEL_REMOVE_DEVICE: 751 { 752 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_CANCEL_REMOVE_DEVICE\n"); 753 return ForwardIrpAndForget(DeviceObject, Irp); 754 } 755 case IRP_MN_REMOVE_DEVICE: 756 { 757 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n"); 758 Status = ForwardIrpAndForget(DeviceObject, Irp); 759 i8042RemoveDevice(DeviceObject); 760 return Status; 761 } 762 default: 763 { 764 ERR_(I8042PRT, "IRP_MJ_PNP / unknown minor function 0x%x\n", MinorFunction); 765 return ForwardIrpAndForget(DeviceObject, Irp); 766 } 767 } 768 769 Irp->IoStatus.Information = Information; 770 Irp->IoStatus.Status = Status; 771 IoCompleteRequest(Irp, IO_NO_INCREMENT); 772 return Status; 773 } 774