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>
15 #include <ntos/keyboard.h>
16 #include <ntos/minmax.h>
17 #include <rosrtl/string.h>
19 #include <ddk/ntddkbd.h>
20 #include <ddk/ntdd8042.h>
27 /* GLOBALS *******************************************************************/
32 #define I8042_TIMEOUT 500000
34 #define I8042_MAX_COMMAND_LENGTH 16
35 #define I8042_MAX_UPWARDS_STACK 5
37 /* FUNCTIONS *****************************************************************/
40 * FUNCTION: Write data to a port, waiting first for it to become ready
42 BOOLEAN
I8042Write(PDEVICE_EXTENSION DevExt
, int addr
, BYTE data
)
44 ULONG ResendIterations
= DevExt
->Settings
.PollingIterations
;
46 while ((KBD_IBF
& READ_PORT_UCHAR((PUCHAR
)I8042_CTRL_PORT
)) &&
49 KeStallExecutionProcessor(50);
52 if (ResendIterations
) {
53 WRITE_PORT_UCHAR((PUCHAR
)addr
,data
);
54 DPRINT("Sent %x to %x\n", data
, addr
);
60 #if 0 /* function is not needed */
62 * FUNCTION: Write data to a port, without waiting first
64 static BOOLEAN
I8042WriteNoWait(PDEVICE_EXTENSION DevExt
, int addr
, BYTE data
)
66 WRITE_PORT_UCHAR((PUCHAR
)addr
,data
);
67 DPRINT("Sent %x to %x\n", data
, addr
);
73 * FUNCTION: Read data from port 0x60
75 NTSTATUS
I8042ReadData(BYTE
*Data
)
78 Status
=READ_PORT_UCHAR((PUCHAR
)I8042_CTRL_PORT
);
80 // If data is available
81 if ((Status
& KBD_OBF
)) {
82 Data
[0]=READ_PORT_UCHAR((PUCHAR
)I8042_DATA_PORT
);
83 DPRINT("Read: %x (status: %x)\n", Data
[0], Status
);
85 // If the data is valid (not timeout, not parity error)
86 if (0 == (Status
& (KBD_GTO
| KBD_PERR
)))
87 return STATUS_SUCCESS
;
89 return STATUS_UNSUCCESSFUL
;
92 NTSTATUS
I8042ReadStatus(BYTE
*Status
)
94 Status
[0]=READ_PORT_UCHAR((PUCHAR
)I8042_CTRL_PORT
);
95 return STATUS_SUCCESS
;
99 * FUNCTION: Read data from port 0x60
101 NTSTATUS
I8042ReadDataWait(PDEVICE_EXTENSION DevExt
, BYTE
*Data
)
103 ULONG Counter
= DevExt
->Settings
.PollingIterations
;
107 Status
= I8042ReadData(Data
);
109 if (STATUS_SUCCESS
== Status
)
112 KeStallExecutionProcessor(50);
115 return STATUS_IO_TIMEOUT
;
122 while (STATUS_SUCCESS
== I8042ReadData(&Ignore
)) {
127 VOID STDCALL
I8042IsrWritePort(PDEVICE_EXTENSION DevExt
,
132 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, SelectCmd
))
135 I8042Write(DevExt
, I8042_DATA_PORT
, Value
);
139 * These functions are callbacks for filter driver custom
140 * initialization routines.
142 NTSTATUS STDCALL
I8042SynchWritePort(PDEVICE_EXTENSION DevExt
,
149 UINT ResendIterations
= DevExt
->Settings
.ResendIterations
+ 1;
153 if (!I8042Write(DevExt
, I8042_DATA_PORT
, Port
))
154 return STATUS_TIMEOUT
;
156 if (!I8042Write(DevExt
, I8042_DATA_PORT
, Value
))
157 return STATUS_TIMEOUT
;
160 Status
= I8042ReadDataWait(DevExt
, &Ack
);
161 if (Status
!= STATUS_SUCCESS
)
164 return STATUS_SUCCESS
;
165 if (Ack
!= KBD_RESEND
)
166 return STATUS_UNEXPECTED_IO_ERROR
;
168 return STATUS_SUCCESS
;
171 } while (ResendIterations
);
172 return STATUS_TIMEOUT
;
176 * This one reads a value from the port; You don't have to specify
177 * which one, it'll always be from the one you talked to, so one function
178 * is enough this time. Note how MSDN specifies the
179 * WaitForAck parameter to be ignored.
181 static NTSTATUS STDCALL
I8042SynchReadPort(PVOID Context
,
185 PDEVICE_EXTENSION DevExt
= (PDEVICE_EXTENSION
)Context
;
187 return I8042ReadDataWait(DevExt
, Value
);
190 /* Write the current byte of the packet. Returns FALSE in case
193 static BOOLEAN STDCALL
I8042PacketWrite(PDEVICE_EXTENSION DevExt
)
195 UCHAR Port
= DevExt
->PacketPort
;
198 if (!I8042Write(DevExt
,
201 /* something is really wrong! */
202 DPRINT1("Failed to send packet byte!\n");
207 return I8042Write(DevExt
,
209 DevExt
->Packet
.Bytes
[DevExt
->Packet
.CurrentByte
]);
214 * This function starts a packet. It must be called with the
217 NTSTATUS STDCALL
I8042StartPacket(PDEVICE_EXTENSION DevExt
,
218 PDEVICE_OBJECT Device
,
225 PFDO_DEVICE_EXTENSION FdoDevExt
= Device
->DeviceExtension
;
227 Irql
= KeAcquireInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
);
229 DevExt
->CurrentIrp
= Irp
;
230 DevExt
->CurrentIrpDevice
= Device
;
232 if (Idle
!= DevExt
->Packet
.State
) {
233 Status
= STATUS_DEVICE_BUSY
;
234 goto startpacketdone
;
237 DevExt
->Packet
.Bytes
= Bytes
;
238 DevExt
->Packet
.CurrentByte
= 0;
239 DevExt
->Packet
.ByteCount
= ByteCount
;
240 DevExt
->Packet
.State
= SendingBytes
;
241 DevExt
->PacketResult
= Status
= STATUS_PENDING
;
243 if (Mouse
== FdoDevExt
->Type
)
244 DevExt
->PacketPort
= 0xD4;
246 DevExt
->PacketPort
= 0;
248 if (!I8042PacketWrite(DevExt
)) {
249 Status
= STATUS_TIMEOUT
;
250 DevExt
->Packet
.State
= Idle
;
251 DevExt
->PacketResult
= STATUS_ABANDONED
;
252 goto startpacketdone
;
255 DevExt
->Packet
.CurrentByte
++;
258 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
, Irql
);
260 if (STATUS_PENDING
!= Status
) {
261 DevExt
->CurrentIrp
= NULL
;
262 DevExt
->CurrentIrpDevice
= NULL
;
263 Irp
->IoStatus
.Status
= Status
;
264 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
269 BOOLEAN STDCALL
I8042PacketIsr(PDEVICE_EXTENSION DevExt
,
272 if (Idle
== DevExt
->Packet
.State
)
277 DevExt
->PacketResends
++;
278 if (DevExt
->PacketResends
> DevExt
->Settings
.ResendIterations
) {
279 DevExt
->Packet
.State
= Idle
;
280 DevExt
->PacketComplete
= TRUE
;
281 DevExt
->PacketResult
= STATUS_TIMEOUT
;
282 DevExt
->PacketResends
= 0;
285 DevExt
->Packet
.CurrentByte
--;
289 DevExt
->Packet
.State
= Idle
;
290 DevExt
->PacketComplete
= TRUE
;
291 DevExt
->PacketResult
= STATUS_UNEXPECTED_IO_ERROR
;
292 DevExt
->PacketResends
= 0;
296 DevExt
->PacketResends
= 0;
299 if (DevExt
->Packet
.CurrentByte
>= DevExt
->Packet
.ByteCount
) {
300 DevExt
->Packet
.State
= Idle
;
301 DevExt
->PacketComplete
= TRUE
;
302 DevExt
->PacketResult
= STATUS_SUCCESS
;
306 if (!I8042PacketWrite(DevExt
)) {
307 DevExt
->Packet
.State
= Idle
;
308 DevExt
->PacketComplete
= TRUE
;
309 DevExt
->PacketResult
= STATUS_TIMEOUT
;
312 DevExt
->Packet
.CurrentByte
++;
317 VOID
I8042PacketDpc(PDEVICE_EXTENSION DevExt
)
319 BOOL FinishIrp
= FALSE
;
320 NTSTATUS Result
= STATUS_INTERNAL_ERROR
; /* Shouldn't happen */
323 /* If the interrupt happens before this is setup, the key
324 * was already in the buffer. Too bad! */
325 if (!DevExt
->HighestDIRQLInterrupt
)
328 Irql
= KeAcquireInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
);
330 if (Idle
== DevExt
->Packet
.State
&&
331 DevExt
->PacketComplete
) {
333 Result
= DevExt
->PacketResult
;
334 DevExt
->PacketComplete
= FALSE
;
337 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
,
343 if (DevExt
->CurrentIrp
) {
344 DevExt
->CurrentIrp
->IoStatus
.Status
= Result
;
345 IoCompleteRequest(DevExt
->CurrentIrp
, IO_NO_INCREMENT
);
346 IoStartNextPacket(DevExt
->CurrentIrpDevice
, FALSE
);
347 DevExt
->CurrentIrp
= NULL
;
348 DevExt
->CurrentIrpDevice
= NULL
;
352 VOID STDCALL
I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject
,
356 IO_STATUS_BLOCK IoStatus
;
358 PDEVICE_EXTENSION DevExt
;
359 PFDO_DEVICE_EXTENSION FdoDevExt
;
361 PI8042_HOOK_WORKITEM WorkItemData
= (PI8042_HOOK_WORKITEM
)Context
;
365 ULONG InputBufferLength
;
368 DPRINT("HookWorkItem\n");
370 FdoDevExt
= (PFDO_DEVICE_EXTENSION
)
371 DeviceObject
->DeviceExtension
;
373 DevExt
= FdoDevExt
->PortDevExt
;
375 if (WorkItemData
->Target
== DevExt
->KeyboardData
.ClassDeviceObject
) {
376 IoControlCode
= IOCTL_INTERNAL_I8042_HOOK_KEYBOARD
;
377 InputBuffer
= &DevExt
->KeyboardHook
;
378 InputBufferLength
= sizeof(INTERNAL_I8042_HOOK_KEYBOARD
);
380 DPRINT ("is for keyboard.\n");
381 } else if (WorkItemData
->Target
== DevExt
->MouseData
.ClassDeviceObject
){
382 IoControlCode
= IOCTL_INTERNAL_I8042_HOOK_MOUSE
;
383 InputBuffer
= &DevExt
->MouseHook
;
384 InputBufferLength
= sizeof(INTERNAL_I8042_HOOK_MOUSE
);
386 DPRINT ("is for mouse.\n");
388 DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n");
389 WorkItemData
->Irp
->IoStatus
.Status
= STATUS_INTERNAL_ERROR
;
390 goto hookworkitemdone
;
393 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
395 NewIrp
= IoBuildDeviceIoControlRequest(
397 WorkItemData
->Target
,
407 DPRINT("IOCTL_INTERNAL_(device)_CONNECT: "
408 "Can't allocate IRP\n");
409 WorkItemData
->Irp
->IoStatus
.Status
=
410 STATUS_INSUFFICIENT_RESOURCES
;
411 goto hookworkitemdone
;
414 Status
= IoCallDriver(
415 WorkItemData
->Target
,
418 if (STATUS_PENDING
== Status
)
419 KeWaitForSingleObject(&Event
,
426 /* Call the hooked initialization if it exists */
427 if (DevExt
->KeyboardHook
.InitializationRoutine
) {
428 Status
= DevExt
->KeyboardHook
.InitializationRoutine(
429 DevExt
->KeyboardHook
.Context
,
432 I8042SynchWritePortKbd
,
434 if (Status
!= STATUS_SUCCESS
) {
435 WorkItemData
->Irp
->IoStatus
.Status
= Status
;
436 goto hookworkitemdone
;
439 /* TODO: Now would be the right time to enable the interrupt */
441 DevExt
->KeyboardClaimed
= TRUE
;
443 /* Mouse doesn't have this, but we need to send a
444 * reset to start the detection.
448 Irql
= KeAcquireInterruptSpinLock(
449 DevExt
->HighestDIRQLInterrupt
);
451 I8042Write(DevExt
, I8042_CTRL_PORT
, 0xD4);
452 I8042Write(DevExt
, I8042_DATA_PORT
, 0xFF);
454 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
, Irql
);
457 WorkItemData
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
460 WorkItemData
->Irp
->IoStatus
.Information
= 0;
461 IoCompleteRequest(WorkItemData
->Irp
, IO_NO_INCREMENT
);
463 IoFreeWorkItem(WorkItemData
->WorkItem
);
464 ExFreePool(WorkItemData
);
465 DPRINT("HookWorkItem done\n");
468 VOID STDCALL
I8042StartIo(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
470 if (!I8042StartIoKbd(DeviceObject
, Irp
)) {
471 DPRINT1("Unhandled StartIo!\n");
475 NTSTATUS STDCALL
I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
477 NTSTATUS Status
= STATUS_INVALID_DEVICE_REQUEST
;
478 PFDO_DEVICE_EXTENSION FdoDevExt
= DeviceObject
->DeviceExtension
;
480 DPRINT("InternalDeviceControl\n");
482 switch (FdoDevExt
->Type
) {
484 Status
= I8042InternalDeviceControlKbd(DeviceObject
, Irp
);
487 Status
= I8042InternalDeviceControlMouse(DeviceObject
, Irp
);
491 if (Status
== STATUS_INVALID_DEVICE_REQUEST
) {
492 DPRINT1("Invalid internal device request!\n");
495 if (Status
!= STATUS_PENDING
)
496 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
501 NTSTATUS STDCALL
I8042CreateDispatch(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
505 DPRINT ("I8042CreateDispatch\n");
507 Status
= STATUS_SUCCESS
;
509 Irp
->IoStatus
.Status
= Status
;
510 Irp
->IoStatus
.Information
= 0;
511 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
516 static NTSTATUS STDCALL
I8042BasicDetect(PDEVICE_EXTENSION DevExt
)
524 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, KBD_SELF_TEST
))
525 return STATUS_TIMEOUT
;
530 Status
= I8042ReadDataWait(DevExt
, &Value
);
531 } while ((Counter
--) && (STATUS_TIMEOUT
== Status
));
533 if (Status
!= STATUS_SUCCESS
)
537 DPRINT1("Got %x instead of 55\n", Value
);
538 return STATUS_IO_DEVICE_ERROR
;
540 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, KBD_LINE_TEST
))
541 return STATUS_TIMEOUT
;
543 Status
= I8042ReadDataWait(DevExt
, &Value
);
544 if (Status
!= STATUS_SUCCESS
)
548 DevExt
->KeyboardExists
= TRUE
;
550 DevExt
->KeyboardExists
= FALSE
;
553 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, MOUSE_LINE_TEST
))
554 return STATUS_TIMEOUT
;
556 Status
= I8042ReadDataWait(DevExt
, &Value
);
557 if (Status
!= STATUS_SUCCESS
)
561 DevExt
->MouseExists
= TRUE
;
563 DevExt
->MouseExists
= FALSE
;
566 return STATUS_SUCCESS
;
569 static NTSTATUS STDCALL
I8042Initialize(PDEVICE_EXTENSION DevExt
)
573 Status
= I8042BasicDetect(DevExt
);
574 if (Status
!= STATUS_SUCCESS
) {
575 DPRINT1("Basic keyboard detection failed: %x\n", Status
);
579 if (!DevExt
->KeyboardExists
) {
580 DPRINT("Keyboard not detected\n")
581 if (DevExt
->Settings
.Headless
)
582 /* Act as if it exists regardless */
583 DevExt
->KeyboardExists
= TRUE
;
585 DPRINT("Keyboard detected\n");
586 DevExt
->KeyboardExists
= I8042DetectKeyboard(DevExt
);
589 if (DevExt
->KeyboardExists
) {
590 I8042KeyboardEnable(DevExt
);
591 I8042KeyboardEnableInterrupt(DevExt
);
594 if (DevExt
->MouseExists
)
595 I8042MouseEnable(DevExt
);
597 return STATUS_SUCCESS
;
600 static NTSTATUS STDCALL
I8042AddDevice(PDRIVER_OBJECT DriverObject
,
603 UNICODE_STRING DeviceName
= ROS_STRING_INITIALIZER(L
"\\Device\\KeyboardClass0");
604 UNICODE_STRING MouseName
= ROS_STRING_INITIALIZER(L
"\\Device\\PointerClass0");
605 ULONG MappedIrqKeyboard
= 0, MappedIrqMouse
= 0;
606 KIRQL DirqlKeyboard
= 0;
607 KIRQL DirqlMouse
= 0;
611 PDEVICE_EXTENSION DevExt
;
612 PFDO_DEVICE_EXTENSION FdoDevExt
;
615 DPRINT("I8042AddDevice\n");
617 Status
= IoCreateDevice(DriverObject
,
618 sizeof(DEVICE_EXTENSION
),
620 FILE_DEVICE_8042_PORT
,
621 FILE_DEVICE_SECURE_OPEN
,
625 if (!NT_SUCCESS(Status
))
628 IoAttachDeviceToDeviceStack(Fdo
, Pdo
);
630 DevExt
= Fdo
->DeviceExtension
;
632 RtlZeroMemory(DevExt
, sizeof(DEVICE_EXTENSION
));
634 I8042ReadRegistry(DriverObject
, DevExt
);
636 KeInitializeSpinLock(&DevExt
->SpinLock
);
637 InitializeListHead(&DevExt
->BusDevices
);
639 KeInitializeDpc(&DevExt
->DpcKbd
,
643 KeInitializeDpc(&DevExt
->DpcMouse
,
644 I8042DpcRoutineMouse
,
647 KeInitializeDpc(&DevExt
->DpcMouseTimeout
,
648 I8042DpcRoutineMouseTimeout
,
651 KeInitializeTimer(&DevExt
->TimerMouseTimeout
);
653 Status
= I8042Initialize(DevExt
);
654 if (!NT_SUCCESS(STATUS_SUCCESS
)) {
655 DPRINT1("Initialization failure: %x\n", Status
);
659 if (DevExt
->KeyboardExists
) {
660 MappedIrqKeyboard
= HalGetInterruptVector(Internal
,
667 Status
= IoCreateDevice(DriverObject
,
668 sizeof(FDO_DEVICE_EXTENSION
),
670 FILE_DEVICE_8042_PORT
,
671 FILE_DEVICE_SECURE_OPEN
,
675 if (NT_SUCCESS(Status
))
677 FdoDevExt
= Fdo
->DeviceExtension
;
679 RtlZeroMemory(FdoDevExt
, sizeof(FDO_DEVICE_EXTENSION
));
681 FdoDevExt
->PortDevExt
= DevExt
;
682 FdoDevExt
->Type
= Keyboard
;
683 FdoDevExt
->DeviceObject
= Fdo
;
685 Fdo
->Flags
|= DO_BUFFERED_IO
;
687 DevExt
->DebugWorkItem
= IoAllocateWorkItem(Fdo
);
688 DevExt
->KeyboardObject
= Fdo
;
690 DevExt
->KeyboardBuffer
= ExAllocatePoolWithTag(
692 DevExt
->KeyboardAttributes
.InputDataQueueLength
*
693 sizeof(KEYBOARD_INPUT_DATA
),
696 if (!DevExt
->KeyboardBuffer
) {
697 DPRINT1("No memory for keyboardbuffer\n");
698 return STATUS_INSUFFICIENT_RESOURCES
;
701 InsertTailList(&DevExt
->BusDevices
, &FdoDevExt
->BusDevices
);
704 DevExt
->KeyboardExists
= FALSE
;
707 if (DevExt
->MouseExists
) {
708 MappedIrqMouse
= HalGetInterruptVector(Internal
,
715 Status
= IoCreateDevice(DriverObject
,
716 sizeof(FDO_DEVICE_EXTENSION
),
718 FILE_DEVICE_8042_PORT
,
719 FILE_DEVICE_SECURE_OPEN
,
723 if (NT_SUCCESS(Status
))
725 FdoDevExt
= Fdo
->DeviceExtension
;
727 RtlZeroMemory(FdoDevExt
, sizeof(FDO_DEVICE_EXTENSION
));
729 FdoDevExt
->PortDevExt
= DevExt
;
730 FdoDevExt
->Type
= Mouse
;
731 FdoDevExt
->DeviceObject
= Fdo
;
733 Fdo
->Flags
|= DO_BUFFERED_IO
;
734 DevExt
->MouseObject
= Fdo
;
736 DevExt
->MouseBuffer
= ExAllocatePoolWithTag(
738 DevExt
->MouseAttributes
.InputDataQueueLength
*
739 sizeof(MOUSE_INPUT_DATA
),
742 if (!DevExt
->MouseBuffer
) {
743 ExFreePoolWithTag(DevExt
->KeyboardBuffer
, TAG_I8042
);
744 DPRINT1("No memory for mouse buffer\n");
745 return STATUS_INSUFFICIENT_RESOURCES
;
748 InsertTailList(&DevExt
->BusDevices
, &FdoDevExt
->BusDevices
);
751 DevExt
->MouseExists
= FALSE
;
754 if (DirqlKeyboard
> DirqlMouse
)
755 DirqlMax
= DirqlKeyboard
;
757 DirqlMax
= DirqlMouse
;
759 if (DevExt
->KeyboardExists
) {
760 Status
= IoConnectInterrupt(&DevExt
->KeyboardInterruptObject
,
761 I8042InterruptServiceKbd
,
772 DPRINT("Keyboard Irq Status: %x\n", Status
);
775 if (DevExt
->MouseExists
) {
776 Status
= IoConnectInterrupt(&DevExt
->MouseInterruptObject
,
777 I8042InterruptServiceMouse
,
788 DPRINT("Mouse Irq Status: %x\n", Status
);
791 if (DirqlKeyboard
> DirqlMouse
)
792 DevExt
->HighestDIRQLInterrupt
= DevExt
->KeyboardInterruptObject
;
794 DevExt
->HighestDIRQLInterrupt
= DevExt
->MouseInterruptObject
;
796 DPRINT("I8042AddDevice done\n");
798 return(STATUS_SUCCESS
);
801 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
,
802 PUNICODE_STRING RegistryPath
)
804 * FUNCTION: Module entry point
807 DPRINT("I8042 Driver 0.0.1\n");
809 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = I8042CreateDispatch
;
810 DriverObject
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] =
811 I8042InternalDeviceControl
;
813 DriverObject
->DriverStartIo
= I8042StartIo
;
814 DriverObject
->DriverExtension
->AddDevice
= I8042AddDevice
;
816 return(STATUS_SUCCESS
);