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/i8042prt.c 5 * PURPOSE: Driver entry function 6 * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) 7 Copyright Jason Filby (jasonfilby@yahoo.com) 8 Copyright Martijn Vernooij (o112w8r02@sneakemail.com) 9 Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org) 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include "i8042prt.h" 15 16 #include <debug.h> 17 18 /* FUNCTIONS *****************************************************************/ 19 20 static DRIVER_STARTIO i8042StartIo; 21 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL) 22 static DRIVER_DISPATCH i8042DeviceControl; 23 _Dispatch_type_(IRP_MJ_INTERNAL_DEVICE_CONTROL) 24 static DRIVER_DISPATCH i8042InternalDeviceControl; 25 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL) 26 static DRIVER_DISPATCH i8042SystemControl; 27 _Dispatch_type_(IRP_MJ_POWER) 28 static DRIVER_DISPATCH i8042Power; 29 DRIVER_INITIALIZE DriverEntry; 30 31 NTSTATUS NTAPI 32 i8042AddDevice( 33 IN PDRIVER_OBJECT DriverObject, 34 IN PDEVICE_OBJECT Pdo) 35 { 36 PI8042_DRIVER_EXTENSION DriverExtension; 37 PFDO_DEVICE_EXTENSION DeviceExtension = NULL; 38 PDEVICE_OBJECT Fdo = NULL; 39 ULONG DeviceExtensionSize; 40 NTSTATUS Status; 41 42 TRACE_(I8042PRT, "i8042AddDevice(%p %p)\n", DriverObject, Pdo); 43 44 DriverExtension = (PI8042_DRIVER_EXTENSION)IoGetDriverObjectExtension(DriverObject, DriverObject); 45 46 if (Pdo == NULL) 47 { 48 /* We're getting a NULL Pdo at the first call as 49 * we are a legacy driver. Ignore it */ 50 return STATUS_SUCCESS; 51 } 52 53 /* Create new device object. As we don't know if the device would be a keyboard 54 * or a mouse, we have to allocate the biggest device extension. */ 55 DeviceExtensionSize = MAX(sizeof(I8042_KEYBOARD_EXTENSION), sizeof(I8042_MOUSE_EXTENSION)); 56 Status = IoCreateDevice( 57 DriverObject, 58 DeviceExtensionSize, 59 NULL, 60 Pdo->DeviceType, 61 FILE_DEVICE_SECURE_OPEN, 62 TRUE, 63 &Fdo); 64 if (!NT_SUCCESS(Status)) 65 { 66 WARN_(I8042PRT, "IoCreateDevice() failed with status 0x%08lx\n", Status); 67 goto cleanup; 68 } 69 70 DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension; 71 RtlZeroMemory(DeviceExtension, DeviceExtensionSize); 72 DeviceExtension->Type = Unknown; 73 DeviceExtension->Fdo = Fdo; 74 DeviceExtension->Pdo = Pdo; 75 DeviceExtension->PortDeviceExtension = &DriverExtension->Port; 76 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice); 77 if (!NT_SUCCESS(Status)) 78 { 79 WARN_(I8042PRT, "IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); 80 goto cleanup; 81 } 82 83 ExInterlockedInsertTailList( 84 &DriverExtension->DeviceListHead, 85 &DeviceExtension->ListEntry, 86 &DriverExtension->DeviceListLock); 87 88 Fdo->Flags &= ~DO_DEVICE_INITIALIZING; 89 return STATUS_SUCCESS; 90 91 cleanup: 92 if (DeviceExtension && DeviceExtension->LowerDevice) 93 IoDetachDevice(DeviceExtension->LowerDevice); 94 if (Fdo) 95 IoDeleteDevice(Fdo); 96 return Status; 97 } 98 99 VOID NTAPI 100 i8042SendHookWorkItem( 101 IN PDEVICE_OBJECT DeviceObject, 102 IN PVOID Context) 103 { 104 PI8042_HOOK_WORKITEM WorkItemData; 105 PFDO_DEVICE_EXTENSION FdoDeviceExtension; 106 PPORT_DEVICE_EXTENSION PortDeviceExtension; 107 PDEVICE_OBJECT TopOfStack = NULL; 108 ULONG IoControlCode; 109 PVOID InputBuffer; 110 ULONG InputBufferLength; 111 IO_STATUS_BLOCK IoStatus; 112 KEVENT Event; 113 PIRP NewIrp; 114 NTSTATUS Status; 115 116 TRACE_(I8042PRT, "i8042SendHookWorkItem(%p %p)\n", DeviceObject, Context); 117 118 WorkItemData = (PI8042_HOOK_WORKITEM)Context; 119 FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 120 PortDeviceExtension = FdoDeviceExtension->PortDeviceExtension; 121 122 switch (FdoDeviceExtension->Type) 123 { 124 case Keyboard: 125 { 126 PI8042_KEYBOARD_EXTENSION DeviceExtension; 127 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)FdoDeviceExtension; 128 IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD; 129 InputBuffer = &DeviceExtension->KeyboardHook; 130 InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD); 131 break; 132 } 133 case Mouse: 134 { 135 PI8042_MOUSE_EXTENSION DeviceExtension; 136 DeviceExtension = (PI8042_MOUSE_EXTENSION)FdoDeviceExtension; 137 IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE; 138 InputBuffer = &DeviceExtension->MouseHook; 139 InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE); 140 break; 141 } 142 default: 143 { 144 ERR_(I8042PRT, "Unknown FDO type %u\n", FdoDeviceExtension->Type); 145 ASSERT(FALSE); 146 WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR; 147 goto cleanup; 148 } 149 } 150 151 KeInitializeEvent(&Event, NotificationEvent, FALSE); 152 TopOfStack = IoGetAttachedDeviceReference(DeviceObject); 153 154 NewIrp = IoBuildDeviceIoControlRequest( 155 IoControlCode, 156 TopOfStack, 157 InputBuffer, 158 InputBufferLength, 159 NULL, 160 0, 161 TRUE, 162 &Event, 163 &IoStatus); 164 165 if (!NewIrp) 166 { 167 WARN_(I8042PRT, "IoBuildDeviceIoControlRequest() failed\n"); 168 WorkItemData->Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 169 goto cleanup; 170 } 171 172 Status = IoCallDriver(TopOfStack, NewIrp); 173 if (Status == STATUS_PENDING) 174 { 175 KeWaitForSingleObject( 176 &Event, 177 Executive, 178 KernelMode, 179 FALSE, 180 NULL); 181 Status = IoStatus.Status; 182 } 183 if (!NT_SUCCESS(Status)) 184 { 185 WARN_(I8042PRT, "IoCallDriver() failed with status 0x%08lx\n", Status); 186 goto cleanup; 187 } 188 189 if (FdoDeviceExtension->Type == Keyboard) 190 { 191 PI8042_KEYBOARD_EXTENSION DeviceExtension; 192 193 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)FdoDeviceExtension; 194 /* Call the hooked initialization if it exists */ 195 if (DeviceExtension->KeyboardHook.InitializationRoutine) 196 { 197 Status = DeviceExtension->KeyboardHook.InitializationRoutine( 198 DeviceExtension->KeyboardHook.Context, 199 PortDeviceExtension, 200 i8042SynchReadPort, 201 i8042SynchWritePortKbd, 202 FALSE); 203 if (!NT_SUCCESS(Status)) 204 { 205 WARN_(I8042PRT, "KeyboardHook.InitializationRoutine() failed with status 0x%08lx\n", Status); 206 WorkItemData->Irp->IoStatus.Status = Status; 207 goto cleanup; 208 } 209 } 210 } 211 212 WorkItemData->Irp->IoStatus.Status = STATUS_SUCCESS; 213 214 cleanup: 215 if (TopOfStack != NULL) 216 ObDereferenceObject(TopOfStack); 217 WorkItemData->Irp->IoStatus.Information = 0; 218 IoCompleteRequest(WorkItemData->Irp, IO_NO_INCREMENT); 219 220 IoFreeWorkItem(WorkItemData->WorkItem); 221 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); 222 } 223 224 static VOID NTAPI 225 i8042StartIo( 226 IN PDEVICE_OBJECT DeviceObject, 227 IN PIRP Irp) 228 { 229 PFDO_DEVICE_EXTENSION DeviceExtension; 230 231 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 232 switch (DeviceExtension->Type) 233 { 234 case Keyboard: 235 i8042KbdStartIo(DeviceObject, Irp); 236 break; 237 default: 238 ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type); 239 ASSERT(FALSE); 240 break; 241 } 242 } 243 244 /* Write the current byte of the packet. Returns FALSE in case 245 * of problems. 246 */ 247 static BOOLEAN 248 i8042PacketWrite( 249 IN PPORT_DEVICE_EXTENSION DeviceExtension) 250 { 251 UCHAR Port = DeviceExtension->PacketPort; 252 253 if (Port) 254 { 255 if (!i8042Write(DeviceExtension, 256 DeviceExtension->ControlPort, 257 Port)) 258 { 259 /* something is really wrong! */ 260 WARN_(I8042PRT, "Failed to send packet byte!\n"); 261 return FALSE; 262 } 263 } 264 265 return i8042Write(DeviceExtension, 266 DeviceExtension->DataPort, 267 DeviceExtension->Packet.Bytes[DeviceExtension->Packet.CurrentByte]); 268 } 269 270 BOOLEAN 271 i8042PacketIsr( 272 IN PPORT_DEVICE_EXTENSION DeviceExtension, 273 IN UCHAR Output) 274 { 275 if (DeviceExtension->Packet.State == Idle) 276 return FALSE; 277 278 switch (Output) 279 { 280 case KBD_RESEND: 281 DeviceExtension->PacketResends++; 282 if (DeviceExtension->PacketResends > DeviceExtension->Settings.ResendIterations) 283 { 284 DeviceExtension->Packet.State = Idle; 285 DeviceExtension->PacketComplete = TRUE; 286 DeviceExtension->PacketResult = STATUS_IO_TIMEOUT; 287 DeviceExtension->PacketResends = 0; 288 return TRUE; 289 } 290 DeviceExtension->Packet.CurrentByte--; 291 break; 292 293 case KBD_NACK: 294 DeviceExtension->Packet.State = Idle; 295 DeviceExtension->PacketComplete = TRUE; 296 DeviceExtension->PacketResult = STATUS_UNEXPECTED_IO_ERROR; 297 DeviceExtension->PacketResends = 0; 298 return TRUE; 299 300 default: 301 DeviceExtension->PacketResends = 0; 302 } 303 304 if (DeviceExtension->Packet.CurrentByte >= DeviceExtension->Packet.ByteCount) 305 { 306 DeviceExtension->Packet.State = Idle; 307 DeviceExtension->PacketComplete = TRUE; 308 DeviceExtension->PacketResult = STATUS_SUCCESS; 309 return TRUE; 310 } 311 312 if (!i8042PacketWrite(DeviceExtension)) 313 { 314 DeviceExtension->Packet.State = Idle; 315 DeviceExtension->PacketComplete = TRUE; 316 DeviceExtension->PacketResult = STATUS_IO_TIMEOUT; 317 return TRUE; 318 } 319 DeviceExtension->Packet.CurrentByte++; 320 321 return TRUE; 322 } 323 324 /* 325 * This function starts a packet. It must be called with the 326 * correct DIRQL. 327 */ 328 NTSTATUS 329 i8042StartPacket( 330 IN PPORT_DEVICE_EXTENSION DeviceExtension, 331 IN PFDO_DEVICE_EXTENSION FdoDeviceExtension, 332 IN PUCHAR Bytes, 333 IN ULONG ByteCount, 334 IN PIRP Irp) 335 { 336 KIRQL Irql; 337 NTSTATUS Status; 338 339 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt); 340 341 if (DeviceExtension->Packet.State != Idle) 342 { 343 Status = STATUS_DEVICE_BUSY; 344 goto done; 345 } 346 347 switch (FdoDeviceExtension->Type) 348 { 349 case Keyboard: DeviceExtension->PacketPort = 0; break; 350 case Mouse: DeviceExtension->PacketPort = CTRL_WRITE_MOUSE; break; 351 default: 352 ERR_(I8042PRT, "Unknown FDO type %u\n", FdoDeviceExtension->Type); 353 ASSERT(FALSE); 354 Status = STATUS_INTERNAL_ERROR; 355 goto done; 356 } 357 358 DeviceExtension->Packet.Bytes = Bytes; 359 DeviceExtension->Packet.CurrentByte = 0; 360 DeviceExtension->Packet.ByteCount = ByteCount; 361 DeviceExtension->Packet.State = SendingBytes; 362 DeviceExtension->PacketResult = Status = STATUS_PENDING; 363 DeviceExtension->CurrentIrp = Irp; 364 DeviceExtension->CurrentIrpDevice = FdoDeviceExtension->Fdo; 365 366 if (!i8042PacketWrite(DeviceExtension)) 367 { 368 Status = STATUS_IO_TIMEOUT; 369 DeviceExtension->Packet.State = Idle; 370 DeviceExtension->PacketResult = STATUS_ABANDONED; 371 goto done; 372 } 373 374 DeviceExtension->Packet.CurrentByte++; 375 376 done: 377 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql); 378 379 if (Status != STATUS_PENDING) 380 { 381 DeviceExtension->CurrentIrp = NULL; 382 DeviceExtension->CurrentIrpDevice = NULL; 383 Irp->IoStatus.Status = Status; 384 IoCompleteRequest(Irp, IO_NO_INCREMENT); 385 } 386 return Status; 387 } 388 389 static NTSTATUS NTAPI 390 i8042DeviceControl( 391 IN PDEVICE_OBJECT DeviceObject, 392 IN PIRP Irp) 393 { 394 PFDO_DEVICE_EXTENSION DeviceExtension; 395 396 TRACE_(I8042PRT, "i8042DeviceControl(%p %p)\n", DeviceObject, Irp); 397 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 398 399 switch (DeviceExtension->Type) 400 { 401 case Keyboard: 402 return i8042KbdDeviceControl(DeviceObject, Irp); 403 default: 404 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; 405 IoCompleteRequest(Irp, IO_NO_INCREMENT); 406 return STATUS_INVALID_DEVICE_REQUEST; 407 } 408 } 409 410 static NTSTATUS NTAPI 411 i8042InternalDeviceControl( 412 IN PDEVICE_OBJECT DeviceObject, 413 IN PIRP Irp) 414 { 415 PFDO_DEVICE_EXTENSION DeviceExtension; 416 ULONG ControlCode; 417 NTSTATUS Status; 418 419 TRACE_(I8042PRT, "i8042InternalDeviceControl(%p %p)\n", DeviceObject, Irp); 420 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 421 422 switch (DeviceExtension->Type) 423 { 424 case Unknown: 425 { 426 ControlCode = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode; 427 switch (ControlCode) 428 { 429 case IOCTL_INTERNAL_KEYBOARD_CONNECT: 430 Status = i8042KbdInternalDeviceControl(DeviceObject, Irp); 431 break; 432 case IOCTL_INTERNAL_MOUSE_CONNECT: 433 Status = i8042MouInternalDeviceControl(DeviceObject, Irp); 434 break; 435 default: 436 ERR_(I8042PRT, "Unknown IO control code 0x%lx\n", ControlCode); 437 ASSERT(FALSE); 438 Status = STATUS_INVALID_DEVICE_REQUEST; 439 break; 440 } 441 break; 442 } 443 case Keyboard: 444 Status = i8042KbdInternalDeviceControl(DeviceObject, Irp); 445 break; 446 case Mouse: 447 Status = i8042MouInternalDeviceControl(DeviceObject, Irp); 448 break; 449 default: 450 ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type); 451 ASSERT(FALSE); 452 Status = STATUS_INTERNAL_ERROR; 453 IoCompleteRequest(Irp, IO_NO_INCREMENT); 454 break; 455 } 456 457 return Status; 458 } 459 460 static NTSTATUS NTAPI 461 i8042Power( 462 IN PDEVICE_OBJECT DeviceObject, 463 IN PIRP Irp) 464 { 465 PFDO_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 466 PDEVICE_OBJECT LowerDevice = DeviceExtension->LowerDevice; 467 468 PoStartNextPowerIrp(Irp); 469 IoSkipCurrentIrpStackLocation(Irp); 470 return PoCallDriver(LowerDevice, Irp); 471 } 472 473 static NTSTATUS NTAPI 474 i8042SystemControl( 475 IN PDEVICE_OBJECT DeviceObject, 476 IN PIRP Irp) 477 { 478 return ForwardIrpAndForget(DeviceObject, Irp); 479 } 480 481 NTSTATUS NTAPI 482 DriverEntry( 483 IN PDRIVER_OBJECT DriverObject, 484 IN PUNICODE_STRING RegistryPath) 485 { 486 PI8042_DRIVER_EXTENSION DriverExtension; 487 NTSTATUS Status; 488 489 Status = IoAllocateDriverObjectExtension( 490 DriverObject, 491 DriverObject, 492 sizeof(I8042_DRIVER_EXTENSION), 493 (PVOID*)&DriverExtension); 494 if (!NT_SUCCESS(Status)) 495 { 496 WARN_(I8042PRT, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status); 497 return Status; 498 } 499 RtlZeroMemory(DriverExtension, sizeof(I8042_DRIVER_EXTENSION)); 500 KeInitializeSpinLock(&DriverExtension->Port.SpinLock); 501 InitializeListHead(&DriverExtension->DeviceListHead); 502 KeInitializeSpinLock(&DriverExtension->DeviceListLock); 503 504 Status = DuplicateUnicodeString( 505 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, 506 RegistryPath, 507 &DriverExtension->RegistryPath); 508 if (!NT_SUCCESS(Status)) 509 { 510 WARN_(I8042PRT, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status); 511 return Status; 512 } 513 514 Status = ReadRegistryEntries(&DriverExtension->RegistryPath, &DriverExtension->Port.Settings); 515 if (!NT_SUCCESS(Status)) 516 { 517 WARN_(I8042PRT, "ReadRegistryEntries() failed with status 0x%08lx\n", Status); 518 return Status; 519 } 520 521 DriverObject->DriverExtension->AddDevice = i8042AddDevice; 522 DriverObject->DriverStartIo = i8042StartIo; 523 524 DriverObject->MajorFunction[IRP_MJ_CREATE] = i8042Create; 525 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = i8042Cleanup; 526 DriverObject->MajorFunction[IRP_MJ_CLOSE] = i8042Close; 527 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = i8042DeviceControl; 528 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = i8042InternalDeviceControl; 529 DriverObject->MajorFunction[IRP_MJ_POWER] = i8042Power; 530 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = i8042SystemControl; 531 DriverObject->MajorFunction[IRP_MJ_PNP] = i8042Pnp; 532 533 i8042InitializeHwHacks(); 534 535 return STATUS_SUCCESS; 536 } 537