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 ****************************************************************/
20 /* GLOBALS *******************************************************************/
25 #define I8042_TIMEOUT 500000
27 #define I8042_MAX_COMMAND_LENGTH 16
28 #define I8042_MAX_UPWARDS_STACK 5
30 /* FUNCTIONS *****************************************************************/
33 * FUNCTION: Write data to a port, waiting first for it to become ready
35 BOOLEAN
I8042Write(PDEVICE_EXTENSION DevExt
, PUCHAR addr
, UCHAR data
)
37 ULONG ResendIterations
= DevExt
->Settings
.PollingIterations
;
39 while ((KBD_IBF
& READ_PORT_UCHAR(I8042_CTRL_PORT
)) &&
42 KeStallExecutionProcessor(50);
45 if (ResendIterations
) {
46 WRITE_PORT_UCHAR(addr
,data
);
47 DPRINT("Sent %x to %x\n", data
, addr
);
53 #if 0 /* function is not needed */
55 * FUNCTION: Write data to a port, without waiting first
57 static BOOLEAN
I8042WriteNoWait(PDEVICE_EXTENSION DevExt
, int addr
, UCHAR data
)
59 WRITE_PORT_UCHAR((PUCHAR
)addr
,data
);
60 DPRINT("Sent %x to %x\n", data
, addr
);
66 * FUNCTION: Read data from port 0x60
68 NTSTATUS
I8042ReadData(UCHAR
*Data
)
71 Status
=READ_PORT_UCHAR(I8042_CTRL_PORT
);
73 // If data is available
74 if ((Status
& KBD_OBF
)) {
75 Data
[0]=READ_PORT_UCHAR((PUCHAR
)I8042_DATA_PORT
);
76 DPRINT("Read: %x (status: %x)\n", Data
[0], Status
);
78 // If the data is valid (not timeout, not parity error)
79 if (0 == (Status
& (KBD_GTO
| KBD_PERR
)))
80 return STATUS_SUCCESS
;
82 return STATUS_UNSUCCESSFUL
;
85 NTSTATUS
I8042ReadStatus(UCHAR
*Status
)
87 Status
[0]=READ_PORT_UCHAR(I8042_CTRL_PORT
);
88 return STATUS_SUCCESS
;
92 * FUNCTION: Read data from port 0x60
94 NTSTATUS
I8042ReadDataWait(PDEVICE_EXTENSION DevExt
, UCHAR
*Data
)
96 ULONG Counter
= DevExt
->Settings
.PollingIterations
;
100 Status
= I8042ReadData(Data
);
102 if (STATUS_SUCCESS
== Status
)
105 KeStallExecutionProcessor(50);
108 return STATUS_IO_TIMEOUT
;
115 while (STATUS_SUCCESS
== I8042ReadData(&Ignore
)) {
120 VOID STDCALL
I8042IsrWritePort(PDEVICE_EXTENSION DevExt
,
125 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, SelectCmd
))
128 I8042Write(DevExt
, I8042_DATA_PORT
, Value
);
132 * These functions are callbacks for filter driver custom
133 * initialization routines.
135 NTSTATUS STDCALL
I8042SynchWritePort(PDEVICE_EXTENSION DevExt
,
142 ULONG ResendIterations
= DevExt
->Settings
.ResendIterations
+ 1;
146 if (!I8042Write(DevExt
, I8042_DATA_PORT
, Port
))
147 return STATUS_TIMEOUT
;
149 if (!I8042Write(DevExt
, I8042_DATA_PORT
, Value
))
150 return STATUS_TIMEOUT
;
153 Status
= I8042ReadDataWait(DevExt
, &Ack
);
154 if (!NT_SUCCESS(Status
))
157 return STATUS_SUCCESS
;
158 if (Ack
!= KBD_RESEND
)
159 return STATUS_UNEXPECTED_IO_ERROR
;
161 return STATUS_SUCCESS
;
164 } while (ResendIterations
);
165 return STATUS_TIMEOUT
;
169 * This one reads a value from the port; You don't have to specify
170 * which one, it'll always be from the one you talked to, so one function
171 * is enough this time. Note how MSDN specifies the
172 * WaitForAck parameter to be ignored.
174 static NTSTATUS STDCALL
I8042SynchReadPort(PVOID Context
,
178 PDEVICE_EXTENSION DevExt
= (PDEVICE_EXTENSION
)Context
;
180 return I8042ReadDataWait(DevExt
, Value
);
183 /* Write the current byte of the packet. Returns FALSE in case
186 static BOOLEAN STDCALL
I8042PacketWrite(PDEVICE_EXTENSION DevExt
)
188 UCHAR Port
= DevExt
->PacketPort
;
191 if (!I8042Write(DevExt
,
194 /* something is really wrong! */
195 DPRINT1("Failed to send packet byte!\n");
200 return I8042Write(DevExt
,
202 DevExt
->Packet
.Bytes
[DevExt
->Packet
.CurrentByte
]);
207 * This function starts a packet. It must be called with the
210 NTSTATUS STDCALL
I8042StartPacket(PDEVICE_EXTENSION DevExt
,
211 PDEVICE_OBJECT Device
,
218 PFDO_DEVICE_EXTENSION FdoDevExt
= Device
->DeviceExtension
;
220 Irql
= KeAcquireInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
);
222 DevExt
->CurrentIrp
= Irp
;
223 DevExt
->CurrentIrpDevice
= Device
;
225 if (Idle
!= DevExt
->Packet
.State
) {
226 Status
= STATUS_DEVICE_BUSY
;
227 goto startpacketdone
;
230 DevExt
->Packet
.Bytes
= Bytes
;
231 DevExt
->Packet
.CurrentByte
= 0;
232 DevExt
->Packet
.ByteCount
= ByteCount
;
233 DevExt
->Packet
.State
= SendingBytes
;
234 DevExt
->PacketResult
= Status
= STATUS_PENDING
;
236 if (Mouse
== FdoDevExt
->Type
)
237 DevExt
->PacketPort
= 0xD4;
239 DevExt
->PacketPort
= 0;
241 if (!I8042PacketWrite(DevExt
)) {
242 Status
= STATUS_TIMEOUT
;
243 DevExt
->Packet
.State
= Idle
;
244 DevExt
->PacketResult
= STATUS_ABANDONED
;
245 goto startpacketdone
;
248 DevExt
->Packet
.CurrentByte
++;
251 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
, Irql
);
253 if (STATUS_PENDING
!= Status
) {
254 DevExt
->CurrentIrp
= NULL
;
255 DevExt
->CurrentIrpDevice
= NULL
;
256 Irp
->IoStatus
.Status
= Status
;
257 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
262 BOOLEAN STDCALL
I8042PacketIsr(PDEVICE_EXTENSION DevExt
,
265 if (Idle
== DevExt
->Packet
.State
)
270 DevExt
->PacketResends
++;
271 if (DevExt
->PacketResends
> DevExt
->Settings
.ResendIterations
) {
272 DevExt
->Packet
.State
= Idle
;
273 DevExt
->PacketComplete
= TRUE
;
274 DevExt
->PacketResult
= STATUS_TIMEOUT
;
275 DevExt
->PacketResends
= 0;
278 DevExt
->Packet
.CurrentByte
--;
282 DevExt
->Packet
.State
= Idle
;
283 DevExt
->PacketComplete
= TRUE
;
284 DevExt
->PacketResult
= STATUS_UNEXPECTED_IO_ERROR
;
285 DevExt
->PacketResends
= 0;
289 DevExt
->PacketResends
= 0;
292 if (DevExt
->Packet
.CurrentByte
>= DevExt
->Packet
.ByteCount
) {
293 DevExt
->Packet
.State
= Idle
;
294 DevExt
->PacketComplete
= TRUE
;
295 DevExt
->PacketResult
= STATUS_SUCCESS
;
299 if (!I8042PacketWrite(DevExt
)) {
300 DevExt
->Packet
.State
= Idle
;
301 DevExt
->PacketComplete
= TRUE
;
302 DevExt
->PacketResult
= STATUS_TIMEOUT
;
305 DevExt
->Packet
.CurrentByte
++;
310 VOID
I8042PacketDpc(PDEVICE_EXTENSION DevExt
)
312 BOOLEAN FinishIrp
= FALSE
;
313 NTSTATUS Result
= STATUS_INTERNAL_ERROR
; /* Shouldn't happen */
316 /* If the interrupt happens before this is setup, the key
317 * was already in the buffer. Too bad! */
318 if (!DevExt
->HighestDIRQLInterrupt
)
321 Irql
= KeAcquireInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
);
323 if (Idle
== DevExt
->Packet
.State
&&
324 DevExt
->PacketComplete
) {
326 Result
= DevExt
->PacketResult
;
327 DevExt
->PacketComplete
= FALSE
;
330 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
,
336 if (DevExt
->CurrentIrp
) {
337 DevExt
->CurrentIrp
->IoStatus
.Status
= Result
;
338 IoCompleteRequest(DevExt
->CurrentIrp
, IO_NO_INCREMENT
);
339 IoStartNextPacket(DevExt
->CurrentIrpDevice
, FALSE
);
340 DevExt
->CurrentIrp
= NULL
;
341 DevExt
->CurrentIrpDevice
= NULL
;
345 VOID STDCALL
I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject
,
349 IO_STATUS_BLOCK IoStatus
;
351 PDEVICE_EXTENSION DevExt
;
352 PFDO_DEVICE_EXTENSION FdoDevExt
;
354 PI8042_HOOK_WORKITEM WorkItemData
= (PI8042_HOOK_WORKITEM
)Context
;
358 ULONG InputBufferLength
;
361 DPRINT("HookWorkItem\n");
363 FdoDevExt
= (PFDO_DEVICE_EXTENSION
)
364 DeviceObject
->DeviceExtension
;
366 DevExt
= FdoDevExt
->PortDevExt
;
368 if (WorkItemData
->Target
== DevExt
->KeyboardData
.ClassDeviceObject
) {
369 IoControlCode
= IOCTL_INTERNAL_I8042_HOOK_KEYBOARD
;
370 InputBuffer
= &DevExt
->KeyboardHook
;
371 InputBufferLength
= sizeof(INTERNAL_I8042_HOOK_KEYBOARD
);
373 DPRINT ("is for keyboard.\n");
374 } else if (WorkItemData
->Target
== DevExt
->MouseData
.ClassDeviceObject
){
375 IoControlCode
= IOCTL_INTERNAL_I8042_HOOK_MOUSE
;
376 InputBuffer
= &DevExt
->MouseHook
;
377 InputBufferLength
= sizeof(INTERNAL_I8042_HOOK_MOUSE
);
379 DPRINT ("is for mouse.\n");
381 DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n");
382 WorkItemData
->Irp
->IoStatus
.Status
= STATUS_INTERNAL_ERROR
;
383 goto hookworkitemdone
;
386 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
388 NewIrp
= IoBuildDeviceIoControlRequest(
390 WorkItemData
->Target
,
400 DPRINT("IOCTL_INTERNAL_(device)_CONNECT: "
401 "Can't allocate IRP\n");
402 WorkItemData
->Irp
->IoStatus
.Status
=
403 STATUS_INSUFFICIENT_RESOURCES
;
404 goto hookworkitemdone
;
407 Status
= IoCallDriver(
408 WorkItemData
->Target
,
411 if (STATUS_PENDING
== Status
)
412 KeWaitForSingleObject(&Event
,
419 /* Call the hooked initialization if it exists */
420 if (DevExt
->KeyboardHook
.InitializationRoutine
) {
421 Status
= DevExt
->KeyboardHook
.InitializationRoutine(
422 DevExt
->KeyboardHook
.Context
,
425 I8042SynchWritePortKbd
,
427 if (!NT_SUCCESS(Status
)) {
428 WorkItemData
->Irp
->IoStatus
.Status
= Status
;
429 goto hookworkitemdone
;
432 /* TODO: Now would be the right time to enable the interrupt */
434 DevExt
->KeyboardClaimed
= TRUE
;
436 /* Mouse doesn't have this, but we need to send a
437 * reset to start the detection.
441 Irql
= KeAcquireInterruptSpinLock(
442 DevExt
->HighestDIRQLInterrupt
);
444 I8042Write(DevExt
, I8042_CTRL_PORT
, 0xD4);
445 I8042Write(DevExt
, I8042_DATA_PORT
, 0xFF);
447 KeReleaseInterruptSpinLock(DevExt
->HighestDIRQLInterrupt
, Irql
);
450 WorkItemData
->Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
453 WorkItemData
->Irp
->IoStatus
.Information
= 0;
454 IoCompleteRequest(WorkItemData
->Irp
, IO_NO_INCREMENT
);
456 IoFreeWorkItem(WorkItemData
->WorkItem
);
457 ExFreePool(WorkItemData
);
458 DPRINT("HookWorkItem done\n");
461 VOID STDCALL
I8042StartIo(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
463 if (!I8042StartIoKbd(DeviceObject
, Irp
)) {
464 DPRINT1("Unhandled StartIo!\n");
468 NTSTATUS STDCALL
I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
470 NTSTATUS Status
= STATUS_INVALID_DEVICE_REQUEST
;
471 PFDO_DEVICE_EXTENSION FdoDevExt
= DeviceObject
->DeviceExtension
;
473 DPRINT("InternalDeviceControl\n");
475 switch (FdoDevExt
->Type
) {
477 Status
= I8042InternalDeviceControlKbd(DeviceObject
, Irp
);
480 Status
= I8042InternalDeviceControlMouse(DeviceObject
, Irp
);
484 if (Status
== STATUS_INVALID_DEVICE_REQUEST
) {
485 DPRINT1("Invalid internal device request!\n");
488 if (Status
!= STATUS_PENDING
)
489 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
494 NTSTATUS STDCALL
I8042CreateDispatch(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
498 DPRINT ("I8042CreateDispatch\n");
500 Status
= STATUS_SUCCESS
;
502 Irp
->IoStatus
.Status
= Status
;
503 Irp
->IoStatus
.Information
= 0;
504 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
509 static NTSTATUS STDCALL
I8042BasicDetect(PDEVICE_EXTENSION DevExt
)
515 DevExt
->MouseExists
= FALSE
;
516 DevExt
->KeyboardExists
= FALSE
;
520 if (!I8042Write(DevExt
, I8042_CTRL_PORT
, KBD_SELF_TEST
))
521 return STATUS_TIMEOUT
;
526 Status
= I8042ReadDataWait(DevExt
, &Value
);
527 } while ((Counter
--) && (STATUS_TIMEOUT
== Status
));
529 if (!NT_SUCCESS(Status
))
533 DPRINT1("Got %x instead of 55\n", Value
);
534 return STATUS_IO_DEVICE_ERROR
;
537 if (I8042Write(DevExt
, I8042_CTRL_PORT
, KBD_LINE_TEST
))
539 Status
= I8042ReadDataWait(DevExt
, &Value
);
540 if (NT_SUCCESS(Status
) && Value
== 0)
541 DevExt
->KeyboardExists
= TRUE
;
544 if (I8042Write(DevExt
, I8042_CTRL_PORT
, MOUSE_LINE_TEST
))
546 Status
= I8042ReadDataWait(DevExt
, &Value
);
547 if (NT_SUCCESS(Status
) && Value
== 0)
548 DevExt
->MouseExists
= TRUE
;
551 return STATUS_SUCCESS
;
554 static NTSTATUS STDCALL
I8042Initialize(PDEVICE_EXTENSION DevExt
)
558 Status
= I8042BasicDetect(DevExt
);
559 if (!NT_SUCCESS(Status
)) {
560 DPRINT1("Basic keyboard detection failed: %x\n", Status
);
564 if (!DevExt
->KeyboardExists
) {
565 DPRINT("Keyboard port not detected\n");
566 if (DevExt
->Settings
.Headless
)
567 /* Act as if it exists regardless */
568 DevExt
->KeyboardExists
= TRUE
;
570 DPRINT("Keyboard port detected\n");
571 DevExt
->KeyboardExists
= I8042DetectKeyboard(DevExt
);
574 if (DevExt
->KeyboardExists
) {
575 DPRINT("Keyboard detected\n");
576 I8042KeyboardEnable(DevExt
);
577 I8042KeyboardEnableInterrupt(DevExt
);
580 if (DevExt
->MouseExists
) {
581 DPRINT("Mouse detected\n");
582 I8042MouseEnable(DevExt
);
585 return STATUS_SUCCESS
;
588 static NTSTATUS STDCALL
I8042AddDevice(PDRIVER_OBJECT DriverObject
,
591 UNICODE_STRING DeviceName
= RTL_CONSTANT_STRING(L
"\\Device\\KeyboardClass0");
592 UNICODE_STRING MouseName
= RTL_CONSTANT_STRING(L
"\\Device\\PointerClass0");
593 ULONG MappedIrqKeyboard
= 0, MappedIrqMouse
= 0;
594 KIRQL DirqlKeyboard
= 0;
595 KIRQL DirqlMouse
= 0;
599 PDEVICE_EXTENSION DevExt
;
600 PFDO_DEVICE_EXTENSION FdoDevExt
;
603 DPRINT("I8042AddDevice\n");
605 Status
= IoCreateDevice(DriverObject
,
606 sizeof(DEVICE_EXTENSION
),
608 FILE_DEVICE_8042_PORT
,
609 FILE_DEVICE_SECURE_OPEN
,
613 if (!NT_SUCCESS(Status
))
616 IoAttachDeviceToDeviceStack(Fdo
, Pdo
);
618 DevExt
= Fdo
->DeviceExtension
;
620 RtlZeroMemory(DevExt
, sizeof(DEVICE_EXTENSION
));
622 I8042ReadRegistry(DriverObject
, DevExt
);
624 KeInitializeSpinLock(&DevExt
->SpinLock
);
625 InitializeListHead(&DevExt
->BusDevices
);
627 KeInitializeDpc(&DevExt
->DpcKbd
,
631 KeInitializeDpc(&DevExt
->DpcMouse
,
632 I8042DpcRoutineMouse
,
635 KeInitializeDpc(&DevExt
->DpcMouseTimeout
,
636 I8042DpcRoutineMouseTimeout
,
639 KeInitializeTimer(&DevExt
->TimerMouseTimeout
);
641 Status
= I8042Initialize(DevExt
);
642 if (!NT_SUCCESS(STATUS_SUCCESS
)) {
643 DPRINT1("Initialization failure: %x\n", Status
);
647 if (DevExt
->KeyboardExists
) {
648 MappedIrqKeyboard
= HalGetInterruptVector(Internal
,
655 Status
= IoCreateDevice(DriverObject
,
656 sizeof(FDO_DEVICE_EXTENSION
),
658 FILE_DEVICE_8042_PORT
,
659 FILE_DEVICE_SECURE_OPEN
,
663 if (NT_SUCCESS(Status
))
665 FdoDevExt
= Fdo
->DeviceExtension
;
667 RtlZeroMemory(FdoDevExt
, sizeof(FDO_DEVICE_EXTENSION
));
669 FdoDevExt
->PortDevExt
= DevExt
;
670 FdoDevExt
->Type
= Keyboard
;
671 FdoDevExt
->DeviceObject
= Fdo
;
673 Fdo
->Flags
|= DO_BUFFERED_IO
;
675 DevExt
->DebugWorkItem
= IoAllocateWorkItem(Fdo
);
676 DevExt
->KeyboardObject
= Fdo
;
678 DevExt
->KeyboardBuffer
= ExAllocatePoolWithTag(
680 DevExt
->KeyboardAttributes
.InputDataQueueLength
*
681 sizeof(KEYBOARD_INPUT_DATA
),
684 if (!DevExt
->KeyboardBuffer
) {
685 DPRINT1("No memory for keyboardbuffer\n");
686 return STATUS_INSUFFICIENT_RESOURCES
;
689 InsertTailList(&DevExt
->BusDevices
, &FdoDevExt
->BusDevices
);
692 DevExt
->KeyboardExists
= FALSE
;
695 if (DevExt
->MouseExists
) {
696 MappedIrqMouse
= HalGetInterruptVector(Internal
,
703 Status
= IoCreateDevice(DriverObject
,
704 sizeof(FDO_DEVICE_EXTENSION
),
706 FILE_DEVICE_8042_PORT
,
707 FILE_DEVICE_SECURE_OPEN
,
711 if (NT_SUCCESS(Status
))
713 FdoDevExt
= Fdo
->DeviceExtension
;
715 RtlZeroMemory(FdoDevExt
, sizeof(FDO_DEVICE_EXTENSION
));
717 FdoDevExt
->PortDevExt
= DevExt
;
718 FdoDevExt
->Type
= Mouse
;
719 FdoDevExt
->DeviceObject
= Fdo
;
721 Fdo
->Flags
|= DO_BUFFERED_IO
;
722 DevExt
->MouseObject
= Fdo
;
724 DevExt
->MouseBuffer
= ExAllocatePoolWithTag(
726 DevExt
->MouseAttributes
.InputDataQueueLength
*
727 sizeof(MOUSE_INPUT_DATA
),
730 if (!DevExt
->MouseBuffer
) {
731 ExFreePoolWithTag(DevExt
->KeyboardBuffer
, TAG_I8042
);
732 DPRINT1("No memory for mouse buffer\n");
733 return STATUS_INSUFFICIENT_RESOURCES
;
736 InsertTailList(&DevExt
->BusDevices
, &FdoDevExt
->BusDevices
);
739 DevExt
->MouseExists
= FALSE
;
742 if (DirqlKeyboard
> DirqlMouse
)
743 DirqlMax
= DirqlKeyboard
;
745 DirqlMax
= DirqlMouse
;
747 if (DevExt
->KeyboardExists
) {
748 Status
= IoConnectInterrupt(&DevExt
->KeyboardInterruptObject
,
749 I8042InterruptServiceKbd
,
760 DPRINT("Keyboard Irq Status: %x\n", Status
);
763 if (DevExt
->MouseExists
) {
764 Status
= IoConnectInterrupt(&DevExt
->MouseInterruptObject
,
765 I8042InterruptServiceMouse
,
776 DPRINT("Mouse Irq Status: %x\n", Status
);
779 if (DirqlKeyboard
> DirqlMouse
)
780 DevExt
->HighestDIRQLInterrupt
= DevExt
->KeyboardInterruptObject
;
782 DevExt
->HighestDIRQLInterrupt
= DevExt
->MouseInterruptObject
;
784 DPRINT("I8042AddDevice done\n");
786 return(STATUS_SUCCESS
);
789 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
,
790 PUNICODE_STRING RegistryPath
)
792 * FUNCTION: Module entry point
795 DPRINT("I8042 Driver 0.0.1\n");
797 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = I8042CreateDispatch
;
798 DriverObject
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] =
799 I8042InternalDeviceControl
;
801 DriverObject
->DriverStartIo
= I8042StartIo
;
802 DriverObject
->DriverExtension
->AddDevice
= I8042AddDevice
;
804 return(STATUS_SUCCESS
);