2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/input/i8042prt/i8042prt.c
5 * PURPOSE: i8042 (ps/2 keyboard-mouse controller) driver
6 * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
7 * Jason Filby (jasonfilby@yahoo.com)
11 /* INCLUDES ****************************************************************/
13 #include <ddk/ntddk.h>
14 #include <ddk/ntddkbd.h>
15 #include <ddk/ntdd8042.h>
22 /* GLOBALS *******************************************************************/
27 #define I8042_TIMEOUT 500000
29 #define I8042_MAX_COMMAND_LENGTH 16
30 #define I8042_MAX_UPWARDS_STACK 5
32 /* FUNCTIONS *****************************************************************/
35 * FUNCTION: Write data to a port, waiting first for it to become ready
37 BOOLEAN
I8042Write(PDEVICE_EXTENSION DevExt
, int addr
, BYTE data
)
39 ULONG ResendIterations
= DevExt
->Settings
.PollingIterations
;
41 while ((KBD_IBF
& READ_PORT_UCHAR((PUCHAR
)I8042_CTRL_PORT
)) &&
44 KeStallExecutionProcessor(50);
47 if (ResendIterations
) {
48 WRITE_PORT_UCHAR((PUCHAR
)addr
,data
);
49 DPRINT("Sent %x to %x\n", data
, addr
);
55 #if 0 /* function is not needed */
57 * FUNCTION: Write data to a port, without waiting first
59 static BOOLEAN
I8042WriteNoWait(PDEVICE_EXTENSION DevExt
, int addr
, BYTE data
)
61 WRITE_PORT_UCHAR((PUCHAR
)addr
,data
);
62 DPRINT("Sent %x to %x\n", data
, addr
);
68 * FUNCTION: Read data from port 0x60
70 NTSTATUS
I8042ReadData(BYTE
*Data
)
73 Status
=READ_PORT_UCHAR((PUCHAR
)I8042_CTRL_PORT
);
75 // If data is available
76 if ((Status
& KBD_OBF
)) {
77 Data
[0]=READ_PORT_UCHAR((PUCHAR
)I8042_DATA_PORT
);
78 DPRINT("Read: %x (status: %x)\n", Data
[0], Status
);
80 // If the data is valid (not timeout, not parity error)
81 if (0 == (Status
& (KBD_GTO
| KBD_PERR
)))
82 return STATUS_SUCCESS
;
84 return STATUS_UNSUCCESSFUL
;
87 NTSTATUS
I8042ReadStatus(BYTE
*Status
)
89 Status
[0]=READ_PORT_UCHAR((PUCHAR
)I8042_CTRL_PORT
);
90 return STATUS_SUCCESS
;
94 * FUNCTION: Read data from port 0x60
96 NTSTATUS
I8042ReadDataWait(PDEVICE_EXTENSION DevExt
, BYTE
*Data
)
98 ULONG Counter
= DevExt
->Settings
.PollingIterations
;
102 Status
= I8042ReadData(Data
);
104 if (STATUS_SUCCESS
== Status
)
107 KeStallExecutionProcessor(50);
110 return STATUS_IO_TIMEOUT
;
117 while (STATUS_SUCCESS
== I8042ReadData(&Ignore
)) {
122 VOID STDCALL
I8042IsrWritePort(PDEVICE_EXTENSION DevExt
,
127 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, SelectCmd
))
130 I8042Write(DevExt
, I8042_DATA_PORT
, Value
);
134 * These functions are callbacks for filter driver custom
135 * initialization routines.
137 NTSTATUS STDCALL
I8042SynchWritePort(PDEVICE_EXTENSION DevExt
,
144 UINT ResendIterations
= DevExt
->Settings
.ResendIterations
+ 1;
148 if (!I8042Write(DevExt
, I8042_DATA_PORT
, Port
))
149 return STATUS_TIMEOUT
;
151 if (!I8042Write(DevExt
, I8042_DATA_PORT
, Value
))
152 return STATUS_TIMEOUT
;
155 Status
= I8042ReadDataWait(DevExt
, &Ack
);
156 if (Status
!= STATUS_SUCCESS
)
159 return STATUS_SUCCESS
;
160 if (Ack
!= KBD_RESEND
)
161 return STATUS_UNEXPECTED_IO_ERROR
;
163 return STATUS_SUCCESS
;
166 } while (ResendIterations
);
167 return STATUS_TIMEOUT
;
171 * This one reads a value from the port; You don't have to specify
172 * which one, it'll always be from the one you talked to, so one function
173 * is enough this time. Note how MSDN specifies the
174 * WaitForAck parameter to be ignored.
176 static NTSTATUS STDCALL
I8042SynchReadPort(PVOID Context
,
180 PDEVICE_EXTENSION DevExt
= (PDEVICE_EXTENSION
)Context
;
182 return I8042ReadDataWait(DevExt
, Value
);
185 /* Write the current byte of the packet. Returns FALSE in case
188 static BOOLEAN STDCALL
I8042PacketWrite(PDEVICE_EXTENSION DevExt
)
190 UCHAR Port
= DevExt
->PacketPort
;
193 if (!I8042Write(DevExt
,
196 /* something is really wrong! */
197 DPRINT1("Failed to send packet byte!\n");
202 return I8042Write(DevExt
,
204 DevExt
->Packet
.Bytes
[DevExt
->Packet
.CurrentByte
]);
209 * This function starts a packet. It must be called with the
212 NTSTATUS STDCALL
I8042StartPacket(PDEVICE_EXTENSION DevExt
,
213 PDEVICE_OBJECT Device
,
220 PFDO_DEVICE_EXTENSION FdoDevExt
= Device
->DeviceExtension
;
222 Irql
= KeAcquireInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
);
224 DevExt
->CurrentIrp
= Irp
;
225 DevExt
->CurrentIrpDevice
= Device
;
227 if (Idle
!= DevExt
->Packet
.State
) {
228 Status
= STATUS_DEVICE_BUSY
;
229 goto startpacketdone
;
232 DevExt
->Packet
.Bytes
= Bytes
;
233 DevExt
->Packet
.CurrentByte
= 0;
234 DevExt
->Packet
.ByteCount
= ByteCount
;
235 DevExt
->Packet
.State
= SendingBytes
;
236 DevExt
->PacketResult
= Status
= STATUS_PENDING
;
238 if (Mouse
== FdoDevExt
->Type
)
239 DevExt
->PacketPort
= 0xD4;
241 DevExt
->PacketPort
= 0;
243 if (!I8042PacketWrite(DevExt
)) {
244 Status
= STATUS_TIMEOUT
;
245 DevExt
->Packet
.State
= Idle
;
246 DevExt
->PacketResult
= STATUS_ABANDONED
;
247 goto startpacketdone
;
250 DevExt
->Packet
.CurrentByte
++;
253 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
, Irql
);
255 if (STATUS_PENDING
!= Status
) {
256 DevExt
->CurrentIrp
= NULL
;
257 DevExt
->CurrentIrpDevice
= NULL
;
258 Irp
->IoStatus
.Status
= Status
;
259 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
264 BOOLEAN STDCALL
I8042PacketIsr(PDEVICE_EXTENSION DevExt
,
267 if (Idle
== DevExt
->Packet
.State
)
272 DevExt
->PacketResends
++;
273 if (DevExt
->PacketResends
> DevExt
->Settings
.ResendIterations
) {
274 DevExt
->Packet
.State
= Idle
;
275 DevExt
->PacketComplete
= TRUE
;
276 DevExt
->PacketResult
= STATUS_TIMEOUT
;
277 DevExt
->PacketResends
= 0;
280 DevExt
->Packet
.CurrentByte
--;
284 DevExt
->Packet
.State
= Idle
;
285 DevExt
->PacketComplete
= TRUE
;
286 DevExt
->PacketResult
= STATUS_UNEXPECTED_IO_ERROR
;
287 DevExt
->PacketResends
= 0;
291 DevExt
->PacketResends
= 0;
294 if (DevExt
->Packet
.CurrentByte
>= DevExt
->Packet
.ByteCount
) {
295 DevExt
->Packet
.State
= Idle
;
296 DevExt
->PacketComplete
= TRUE
;
297 DevExt
->PacketResult
= STATUS_SUCCESS
;
301 if (!I8042PacketWrite(DevExt
)) {
302 DevExt
->Packet
.State
= Idle
;
303 DevExt
->PacketComplete
= TRUE
;
304 DevExt
->PacketResult
= STATUS_TIMEOUT
;
307 DevExt
->Packet
.CurrentByte
++;
312 VOID
I8042PacketDpc(PDEVICE_EXTENSION DevExt
)
314 BOOL FinishIrp
= FALSE
;
315 NTSTATUS Result
= STATUS_INTERNAL_ERROR
; /* Shouldn't happen */
318 /* If the interrupt happens before this is setup, the key
319 * was already in the buffer. Too bad! */
320 if (!DevExt
->HighestDIRQLInterrupt
)
323 Irql
= KeAcquireInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
);
325 if (Idle
== DevExt
->Packet
.State
&&
326 DevExt
->PacketComplete
) {
328 Result
= DevExt
->PacketResult
;
329 DevExt
->PacketComplete
= FALSE
;
332 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
,
338 if (DevExt
->CurrentIrp
) {
339 DevExt
->CurrentIrp
->IoStatus
.Status
= Result
;
340 IoCompleteRequest(DevExt
->CurrentIrp
, IO_NO_INCREMENT
);
341 IoStartNextPacket(DevExt
->CurrentIrpDevice
, FALSE
);
342 DevExt
->CurrentIrp
= NULL
;
343 DevExt
->CurrentIrpDevice
= NULL
;
347 VOID STDCALL
I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject
,
351 IO_STATUS_BLOCK IoStatus
;
353 PDEVICE_EXTENSION DevExt
;
354 PFDO_DEVICE_EXTENSION FdoDevExt
;
356 PI8042_HOOK_WORKITEM WorkItemData
= (PI8042_HOOK_WORKITEM
)Context
;
360 ULONG InputBufferLength
;
363 DPRINT("HookWorkItem\n");
365 FdoDevExt
= (PFDO_DEVICE_EXTENSION
)
366 DeviceObject
->DeviceExtension
;
368 DevExt
= FdoDevExt
->PortDevExt
;
370 if (WorkItemData
->Target
== DevExt
->KeyboardData
.ClassDeviceObject
) {
371 IoControlCode
= IOCTL_INTERNAL_I8042_HOOK_KEYBOARD
;
372 InputBuffer
= &DevExt
->KeyboardHook
;
373 InputBufferLength
= sizeof(INTERNAL_I8042_HOOK_KEYBOARD
);
375 DPRINT ("is for keyboard.\n");
376 } else if (WorkItemData
->Target
== DevExt
->MouseData
.ClassDeviceObject
){
377 IoControlCode
= IOCTL_INTERNAL_I8042_HOOK_MOUSE
;
378 InputBuffer
= &DevExt
->MouseHook
;
379 InputBufferLength
= sizeof(INTERNAL_I8042_HOOK_MOUSE
);
381 DPRINT ("is for mouse.\n");
383 DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n");
384 WorkItemData
->Irp
->IoStatus
.Status
= STATUS_INTERNAL_ERROR
;
385 goto hookworkitemdone
;
388 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
390 NewIrp
= IoBuildDeviceIoControlRequest(
392 WorkItemData
->Target
,
402 DPRINT("IOCTL_INTERNAL_(device)_CONNECT: "
403 "Can't allocate IRP\n");
404 WorkItemData
->Irp
->IoStatus
.Status
=
405 STATUS_INSUFFICIENT_RESOURCES
;
406 goto hookworkitemdone
;
409 Status
= IoCallDriver(
410 WorkItemData
->Target
,
413 if (STATUS_PENDING
== Status
)
414 KeWaitForSingleObject(&Event
,
421 /* Call the hooked initialization if it exists */
422 if (DevExt
->KeyboardHook
.InitializationRoutine
) {
423 Status
= DevExt
->KeyboardHook
.InitializationRoutine(
424 DevExt
->KeyboardHook
.Context
,
427 I8042SynchWritePortKbd
,
429 if (Status
!= STATUS_SUCCESS
) {
430 WorkItemData
->Irp
->IoStatus
.Status
= Status
;
431 goto hookworkitemdone
;
434 /* TODO: Now would be the right time to enable the interrupt */
436 DevExt
->KeyboardClaimed
= TRUE
;
438 /* Mouse doesn't have this, but we need to send a
439 * reset to start the detection.
443 Irql
= KeAcquireInterruptSpinLock(
444 DevExt
->HighestDIRQLInterrupt
);
446 I8042Write(DevExt
, I8042_CTRL_PORT
, 0xD4);
447 I8042Write(DevExt
, I8042_DATA_PORT
, 0xFF);
449 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
, Irql
);
452 WorkItemData
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
455 WorkItemData
->Irp
->IoStatus
.Information
= 0;
456 IoCompleteRequest(WorkItemData
->Irp
, IO_NO_INCREMENT
);
458 IoFreeWorkItem(WorkItemData
->WorkItem
);
459 ExFreePool(WorkItemData
);
460 DPRINT("HookWorkItem done\n");
463 VOID STDCALL
I8042StartIo(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
465 if (!I8042StartIoKbd(DeviceObject
, Irp
)) {
466 DPRINT1("Unhandled StartIo!\n");
470 NTSTATUS STDCALL
I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
472 NTSTATUS Status
= STATUS_INVALID_DEVICE_REQUEST
;
473 PFDO_DEVICE_EXTENSION FdoDevExt
= DeviceObject
->DeviceExtension
;
475 DPRINT("InternalDeviceControl\n");
477 switch (FdoDevExt
->Type
) {
479 Status
= I8042InternalDeviceControlKbd(DeviceObject
, Irp
);
482 Status
= I8042InternalDeviceControlMouse(DeviceObject
, Irp
);
486 if (Status
== STATUS_INVALID_DEVICE_REQUEST
) {
487 DPRINT1("Invalid internal device request!\n");
490 if (Status
!= STATUS_PENDING
)
491 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
496 NTSTATUS STDCALL
I8042CreateDispatch(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
500 DPRINT ("I8042CreateDispatch\n");
502 Status
= STATUS_SUCCESS
;
504 Irp
->IoStatus
.Status
= Status
;
505 Irp
->IoStatus
.Information
= 0;
506 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
511 static NTSTATUS STDCALL
I8042BasicDetect(PDEVICE_EXTENSION DevExt
)
519 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, KBD_SELF_TEST
))
520 return STATUS_TIMEOUT
;
525 Status
= I8042ReadDataWait(DevExt
, &Value
);
526 } while ((Counter
--) && (STATUS_TIMEOUT
== Status
));
528 if (Status
!= STATUS_SUCCESS
)
532 DPRINT1("Got %x instead of 55\n", Value
);
533 return STATUS_IO_DEVICE_ERROR
;
535 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, KBD_LINE_TEST
))
536 return STATUS_TIMEOUT
;
538 Status
= I8042ReadDataWait(DevExt
, &Value
);
539 if (Status
!= STATUS_SUCCESS
)
543 DevExt
->KeyboardExists
= TRUE
;
545 DevExt
->KeyboardExists
= FALSE
;
548 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, MOUSE_LINE_TEST
))
549 return STATUS_TIMEOUT
;
551 Status
= I8042ReadDataWait(DevExt
, &Value
);
552 if (Status
!= STATUS_SUCCESS
)
556 DevExt
->MouseExists
= TRUE
;
558 DevExt
->MouseExists
= FALSE
;
561 return STATUS_SUCCESS
;
564 static NTSTATUS STDCALL
I8042Initialize(PDEVICE_EXTENSION DevExt
)
568 Status
= I8042BasicDetect(DevExt
);
569 if (Status
!= STATUS_SUCCESS
) {
570 DPRINT1("Basic keyboard detection failed: %x\n", Status
);
574 if (!DevExt
->KeyboardExists
) {
575 DPRINT("Keyboard not detected\n")
576 if (DevExt
->Settings
.Headless
)
577 /* Act as if it exists regardless */
578 DevExt
->KeyboardExists
= TRUE
;
580 DPRINT("Keyboard detected\n");
581 DevExt
->KeyboardExists
= I8042DetectKeyboard(DevExt
);
584 if (DevExt
->KeyboardExists
) {
585 I8042KeyboardEnable(DevExt
);
586 I8042KeyboardEnableInterrupt(DevExt
);
589 if (DevExt
->MouseExists
)
590 I8042MouseEnable(DevExt
);
592 return STATUS_SUCCESS
;
595 static NTSTATUS STDCALL
I8042AddDevice(PDRIVER_OBJECT DriverObject
,
598 UNICODE_STRING DeviceName
= RTL_CONSTANT_STRING(L
"\\Device\\KeyboardClass0");
599 UNICODE_STRING MouseName
= RTL_CONSTANT_STRING(L
"\\Device\\PointerClass0");
600 ULONG MappedIrqKeyboard
= 0, MappedIrqMouse
= 0;
601 KIRQL DirqlKeyboard
= 0;
602 KIRQL DirqlMouse
= 0;
606 PDEVICE_EXTENSION DevExt
;
607 PFDO_DEVICE_EXTENSION FdoDevExt
;
610 DPRINT("I8042AddDevice\n");
612 Status
= IoCreateDevice(DriverObject
,
613 sizeof(DEVICE_EXTENSION
),
615 FILE_DEVICE_8042_PORT
,
616 FILE_DEVICE_SECURE_OPEN
,
620 if (!NT_SUCCESS(Status
))
623 IoAttachDeviceToDeviceStack(Fdo
, Pdo
);
625 DevExt
= Fdo
->DeviceExtension
;
627 RtlZeroMemory(DevExt
, sizeof(DEVICE_EXTENSION
));
629 I8042ReadRegistry(DriverObject
, DevExt
);
631 KeInitializeSpinLock(&DevExt
->SpinLock
);
632 InitializeListHead(&DevExt
->BusDevices
);
634 KeInitializeDpc(&DevExt
->DpcKbd
,
638 KeInitializeDpc(&DevExt
->DpcMouse
,
639 I8042DpcRoutineMouse
,
642 KeInitializeDpc(&DevExt
->DpcMouseTimeout
,
643 I8042DpcRoutineMouseTimeout
,
646 KeInitializeTimer(&DevExt
->TimerMouseTimeout
);
648 Status
= I8042Initialize(DevExt
);
649 if (!NT_SUCCESS(STATUS_SUCCESS
)) {
650 DPRINT1("Initialization failure: %x\n", Status
);
654 if (DevExt
->KeyboardExists
) {
655 MappedIrqKeyboard
= HalGetInterruptVector(Internal
,
662 Status
= IoCreateDevice(DriverObject
,
663 sizeof(FDO_DEVICE_EXTENSION
),
665 FILE_DEVICE_8042_PORT
,
666 FILE_DEVICE_SECURE_OPEN
,
670 if (NT_SUCCESS(Status
))
672 FdoDevExt
= Fdo
->DeviceExtension
;
674 RtlZeroMemory(FdoDevExt
, sizeof(FDO_DEVICE_EXTENSION
));
676 FdoDevExt
->PortDevExt
= DevExt
;
677 FdoDevExt
->Type
= Keyboard
;
678 FdoDevExt
->DeviceObject
= Fdo
;
680 Fdo
->Flags
|= DO_BUFFERED_IO
;
682 DevExt
->DebugWorkItem
= IoAllocateWorkItem(Fdo
);
683 DevExt
->KeyboardObject
= Fdo
;
685 DevExt
->KeyboardBuffer
= ExAllocatePoolWithTag(
687 DevExt
->KeyboardAttributes
.InputDataQueueLength
*
688 sizeof(KEYBOARD_INPUT_DATA
),
691 if (!DevExt
->KeyboardBuffer
) {
692 DPRINT1("No memory for keyboardbuffer\n");
693 return STATUS_INSUFFICIENT_RESOURCES
;
696 InsertTailList(&DevExt
->BusDevices
, &FdoDevExt
->BusDevices
);
699 DevExt
->KeyboardExists
= FALSE
;
702 if (DevExt
->MouseExists
) {
703 MappedIrqMouse
= HalGetInterruptVector(Internal
,
710 Status
= IoCreateDevice(DriverObject
,
711 sizeof(FDO_DEVICE_EXTENSION
),
713 FILE_DEVICE_8042_PORT
,
714 FILE_DEVICE_SECURE_OPEN
,
718 if (NT_SUCCESS(Status
))
720 FdoDevExt
= Fdo
->DeviceExtension
;
722 RtlZeroMemory(FdoDevExt
, sizeof(FDO_DEVICE_EXTENSION
));
724 FdoDevExt
->PortDevExt
= DevExt
;
725 FdoDevExt
->Type
= Mouse
;
726 FdoDevExt
->DeviceObject
= Fdo
;
728 Fdo
->Flags
|= DO_BUFFERED_IO
;
729 DevExt
->MouseObject
= Fdo
;
731 DevExt
->MouseBuffer
= ExAllocatePoolWithTag(
733 DevExt
->MouseAttributes
.InputDataQueueLength
*
734 sizeof(MOUSE_INPUT_DATA
),
737 if (!DevExt
->MouseBuffer
) {
738 ExFreePoolWithTag(DevExt
->KeyboardBuffer
, TAG_I8042
);
739 DPRINT1("No memory for mouse buffer\n");
740 return STATUS_INSUFFICIENT_RESOURCES
;
743 InsertTailList(&DevExt
->BusDevices
, &FdoDevExt
->BusDevices
);
746 DevExt
->MouseExists
= FALSE
;
749 if (DirqlKeyboard
> DirqlMouse
)
750 DirqlMax
= DirqlKeyboard
;
752 DirqlMax
= DirqlMouse
;
754 if (DevExt
->KeyboardExists
) {
755 Status
= IoConnectInterrupt(&DevExt
->KeyboardInterruptObject
,
756 I8042InterruptServiceKbd
,
767 DPRINT("Keyboard Irq Status: %x\n", Status
);
770 if (DevExt
->MouseExists
) {
771 Status
= IoConnectInterrupt(&DevExt
->MouseInterruptObject
,
772 I8042InterruptServiceMouse
,
783 DPRINT("Mouse Irq Status: %x\n", Status
);
786 if (DirqlKeyboard
> DirqlMouse
)
787 DevExt
->HighestDIRQLInterrupt
= DevExt
->KeyboardInterruptObject
;
789 DevExt
->HighestDIRQLInterrupt
= DevExt
->MouseInterruptObject
;
791 DPRINT("I8042AddDevice done\n");
793 return(STATUS_SUCCESS
);
796 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
,
797 PUNICODE_STRING RegistryPath
)
799 * FUNCTION: Module entry point
802 DPRINT("I8042 Driver 0.0.1\n");
804 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = I8042CreateDispatch
;
805 DriverObject
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] =
806 I8042InternalDeviceControl
;
808 DriverObject
->DriverStartIo
= I8042StartIo
;
809 DriverObject
->DriverExtension
->AddDevice
= I8042AddDevice
;
811 return(STATUS_SUCCESS
);