Big move of driver input stack to a Plug-and-Play model:
[reactos.git] / reactos / drivers / input / i8042prt / i8042prt.c
1 /*
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)
8 * Tinus
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #include "i8042prt.h"
17
18 /* GLOBALS *******************************************************************/
19
20 /*
21 * Driver data
22 */
23 #define I8042_TIMEOUT 500000
24
25 #define I8042_MAX_COMMAND_LENGTH 16
26 #define I8042_MAX_UPWARDS_STACK 5
27
28 UNICODE_STRING I8042RegistryPath;
29
30 /* FUNCTIONS *****************************************************************/
31
32 /*
33 * FUNCTION: Write data to a port, waiting first for it to become ready
34 */
35 BOOLEAN I8042Write(PDEVICE_EXTENSION DevExt, PUCHAR addr, UCHAR data)
36 {
37 ULONG ResendIterations = DevExt->Settings.PollingIterations;
38
39 while ((KBD_IBF & READ_PORT_UCHAR(I8042_CTRL_PORT)) &&
40 (ResendIterations--))
41 {
42 KeStallExecutionProcessor(50);
43 }
44
45 if (ResendIterations) {
46 WRITE_PORT_UCHAR(addr,data);
47 DPRINT("Sent %x to %x\n", data, addr);
48 return TRUE;
49 }
50 return FALSE;
51 }
52
53 #if 0 /* function is not needed */
54 /*
55 * FUNCTION: Write data to a port, without waiting first
56 */
57 static BOOLEAN I8042WriteNoWait(PDEVICE_EXTENSION DevExt, int addr, UCHAR data)
58 {
59 WRITE_PORT_UCHAR((PUCHAR)addr,data);
60 DPRINT("Sent %x to %x\n", data, addr);
61 return TRUE;
62 }
63 #endif
64
65 /*
66 * FUNCTION: Read data from port 0x60
67 */
68 NTSTATUS I8042ReadData(UCHAR *Data)
69 {
70 UCHAR Status;
71 Status=READ_PORT_UCHAR(I8042_CTRL_PORT);
72
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);
77
78 // If the data is valid (not timeout, not parity error)
79 if (0 == (Status & KBD_PERR))
80 return STATUS_SUCCESS;
81 }
82 return STATUS_UNSUCCESSFUL;
83 }
84
85 NTSTATUS I8042ReadStatus(UCHAR *Status)
86 {
87 Status[0]=READ_PORT_UCHAR(I8042_CTRL_PORT);
88 return STATUS_SUCCESS;
89 }
90
91 /*
92 * FUNCTION: Read data from port 0x60
93 */
94 NTSTATUS I8042ReadDataWait(PDEVICE_EXTENSION DevExt, UCHAR *Data)
95 {
96 ULONG Counter = DevExt->Settings.PollingIterations;
97 NTSTATUS Status;
98
99 while (Counter--) {
100 Status = I8042ReadData(Data);
101
102 if (STATUS_SUCCESS == Status)
103 return Status;
104
105 KeStallExecutionProcessor(50);
106 }
107 // Timed out
108 return STATUS_IO_TIMEOUT;
109 }
110
111 VOID I8042Flush()
112 {
113 UCHAR Ignore;
114
115 while (STATUS_SUCCESS == I8042ReadData(&Ignore)) {
116 DPRINT("Data flushed\n"); /* drop */
117 }
118 }
119
120 VOID STDCALL I8042IsrWritePort(PDEVICE_EXTENSION DevExt,
121 UCHAR Value,
122 UCHAR SelectCmd)
123 {
124 if (SelectCmd)
125 if (!I8042Write(DevExt, I8042_CTRL_PORT, SelectCmd))
126 return;
127
128 I8042Write(DevExt, I8042_DATA_PORT, Value);
129 }
130
131 /*
132 * These functions are callbacks for filter driver custom
133 * initialization routines.
134 */
135 NTSTATUS STDCALL I8042SynchWritePort(PDEVICE_EXTENSION DevExt,
136 UCHAR Port,
137 UCHAR Value,
138 BOOLEAN WaitForAck)
139 {
140 NTSTATUS Status;
141 UCHAR Ack;
142 ULONG ResendIterations = DevExt->Settings.ResendIterations + 1;
143
144 do {
145 if (Port)
146 if (!I8042Write(DevExt, I8042_DATA_PORT, Port))
147 {
148 DPRINT1("Failed to write Port\n");
149 return STATUS_IO_TIMEOUT;
150 }
151
152 if (!I8042Write(DevExt, I8042_DATA_PORT, Value))
153 {
154 DPRINT1("Failed to write Value\n");
155 return STATUS_IO_TIMEOUT;
156 }
157
158 if (WaitForAck) {
159 Status = I8042ReadDataWait(DevExt, &Ack);
160 if (!NT_SUCCESS(Status))
161 {
162 DPRINT1("Failed to read Ack\n");
163 return Status;
164 }
165 if (Ack == KBD_ACK)
166 return STATUS_SUCCESS;
167 if (Ack != KBD_RESEND)
168 {
169 DPRINT1("Unexpected Ack 0x%x\n", Ack);
170 return STATUS_UNEXPECTED_IO_ERROR;
171 }
172 } else {
173 return STATUS_SUCCESS;
174 }
175 DPRINT("Reiterating\n");
176 ResendIterations--;
177 } while (ResendIterations);
178 return STATUS_IO_TIMEOUT;
179 }
180
181 /*
182 * This one reads a value from the port; You don't have to specify
183 * which one, it'll always be from the one you talked to, so one function
184 * is enough this time. Note how MSDN specifies the
185 * WaitForAck parameter to be ignored.
186 */
187 static NTSTATUS STDCALL I8042SynchReadPort(PVOID Context,
188 PUCHAR Value,
189 BOOLEAN WaitForAck)
190 {
191 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION)Context;
192
193 return I8042ReadDataWait(DevExt, Value);
194 }
195
196 /* Write the current byte of the packet. Returns FALSE in case
197 * of problems.
198 */
199 static BOOLEAN STDCALL I8042PacketWrite(PDEVICE_EXTENSION DevExt)
200 {
201 UCHAR Port = DevExt->PacketPort;
202
203 if (Port) {
204 if (!I8042Write(DevExt,
205 I8042_CTRL_PORT,
206 Port)) {
207 /* something is really wrong! */
208 DPRINT1("Failed to send packet byte!\n");
209 return FALSE;
210 }
211 }
212
213 return I8042Write(DevExt,
214 I8042_DATA_PORT,
215 DevExt->Packet.Bytes[DevExt->Packet.CurrentByte]);
216 }
217
218
219 /*
220 * This function starts a packet. It must be called with the
221 * correct DIRQL.
222 */
223 NTSTATUS STDCALL I8042StartPacket(PDEVICE_EXTENSION DevExt,
224 PDEVICE_OBJECT Device,
225 PUCHAR Bytes,
226 ULONG ByteCount,
227 PIRP Irp)
228 {
229 KIRQL Irql;
230 NTSTATUS Status;
231 PFDO_DEVICE_EXTENSION FdoDevExt = Device->DeviceExtension;
232
233 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
234
235 DevExt->CurrentIrp = Irp;
236 DevExt->CurrentIrpDevice = Device;
237
238 if (Idle != DevExt->Packet.State) {
239 Status = STATUS_DEVICE_BUSY;
240 goto startpacketdone;
241 }
242
243 DevExt->Packet.Bytes = Bytes;
244 DevExt->Packet.CurrentByte = 0;
245 DevExt->Packet.ByteCount = ByteCount;
246 DevExt->Packet.State = SendingBytes;
247 DevExt->PacketResult = Status = STATUS_PENDING;
248
249 if (Mouse == FdoDevExt->Type)
250 DevExt->PacketPort = 0xD4;
251 else
252 DevExt->PacketPort = 0;
253
254 if (!I8042PacketWrite(DevExt)) {
255 Status = STATUS_IO_TIMEOUT;
256 DevExt->Packet.State = Idle;
257 DevExt->PacketResult = STATUS_ABANDONED;
258 goto startpacketdone;
259 }
260
261 DevExt->Packet.CurrentByte++;
262
263 startpacketdone:
264 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
265
266 if (STATUS_PENDING != Status) {
267 DevExt->CurrentIrp = NULL;
268 DevExt->CurrentIrpDevice = NULL;
269 Irp->IoStatus.Status = Status;
270 IoCompleteRequest(Irp, IO_NO_INCREMENT);
271 }
272 return Status;
273 }
274
275 BOOLEAN STDCALL I8042PacketIsr(PDEVICE_EXTENSION DevExt,
276 UCHAR Output)
277 {
278 if (Idle == DevExt->Packet.State)
279 return FALSE;
280
281 switch (Output) {
282 case KBD_RESEND:
283 DevExt->PacketResends++;
284 if (DevExt->PacketResends > DevExt->Settings.ResendIterations) {
285 DevExt->Packet.State = Idle;
286 DevExt->PacketComplete = TRUE;
287 DevExt->PacketResult = STATUS_IO_TIMEOUT;
288 DevExt->PacketResends = 0;
289 return TRUE;
290 }
291 DevExt->Packet.CurrentByte--;
292 break;
293
294 case KBD_NACK:
295 DevExt->Packet.State = Idle;
296 DevExt->PacketComplete = TRUE;
297 DevExt->PacketResult = STATUS_UNEXPECTED_IO_ERROR;
298 DevExt->PacketResends = 0;
299 return TRUE;
300
301 default:
302 DevExt->PacketResends = 0;
303 }
304
305 if (DevExt->Packet.CurrentByte >= DevExt->Packet.ByteCount) {
306 DevExt->Packet.State = Idle;
307 DevExt->PacketComplete = TRUE;
308 DevExt->PacketResult = STATUS_SUCCESS;
309 return TRUE;
310 }
311
312 if (!I8042PacketWrite(DevExt)) {
313 DevExt->Packet.State = Idle;
314 DevExt->PacketComplete = TRUE;
315 DevExt->PacketResult = STATUS_IO_TIMEOUT;
316 return TRUE;
317 }
318 DevExt->Packet.CurrentByte++;
319
320 return TRUE;
321 }
322
323 VOID I8042PacketDpc(PDEVICE_EXTENSION DevExt)
324 {
325 BOOLEAN FinishIrp = FALSE;
326 NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */
327 KIRQL Irql;
328
329 /* If the interrupt happens before this is setup, the key
330 * was already in the buffer. Too bad! */
331 if (!DevExt->HighestDIRQLInterrupt)
332 return;
333
334 Irql = KeAcquireInterruptSpinLock(DevExt->HighestDIRQLInterrupt);
335
336 if (Idle == DevExt->Packet.State &&
337 DevExt->PacketComplete) {
338 FinishIrp = TRUE;
339 Result = DevExt->PacketResult;
340 DevExt->PacketComplete = FALSE;
341 }
342
343 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt,
344 Irql);
345
346 if (!FinishIrp)
347 return;
348
349 if (DevExt->CurrentIrp) {
350 DevExt->CurrentIrp->IoStatus.Status = Result;
351 IoCompleteRequest(DevExt->CurrentIrp, IO_NO_INCREMENT);
352 IoStartNextPacket(DevExt->CurrentIrpDevice, FALSE);
353 DevExt->CurrentIrp = NULL;
354 DevExt->CurrentIrpDevice = NULL;
355 }
356 }
357
358 VOID STDCALL I8042SendHookWorkItem(PDEVICE_OBJECT DeviceObject,
359 PVOID Context)
360 {
361 KEVENT Event;
362 IO_STATUS_BLOCK IoStatus;
363 NTSTATUS Status;
364 PDEVICE_EXTENSION DevExt;
365 PFDO_DEVICE_EXTENSION FdoDevExt;
366 PIRP NewIrp;
367 PI8042_HOOK_WORKITEM WorkItemData = (PI8042_HOOK_WORKITEM)Context;
368
369 ULONG IoControlCode;
370 PVOID InputBuffer;
371 ULONG InputBufferLength;
372 BOOLEAN IsKbd;
373
374 DPRINT("HookWorkItem\n");
375
376 FdoDevExt = (PFDO_DEVICE_EXTENSION)
377 DeviceObject->DeviceExtension;
378
379 DevExt = FdoDevExt->PortDevExt;
380
381 if (WorkItemData->Target == DevExt->KeyboardData.ClassDeviceObject) {
382 IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD;
383 InputBuffer = &DevExt->KeyboardHook;
384 InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD);
385 IsKbd = TRUE;
386 DPRINT ("is for keyboard.\n");
387 } else if (WorkItemData->Target == DevExt->MouseData.ClassDeviceObject){
388 IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE;
389 InputBuffer = &DevExt->MouseHook;
390 InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE);
391 IsKbd = FALSE;
392 DPRINT ("is for mouse.\n");
393 } else {
394 DPRINT1("I8042SendHookWorkItem: Can't find DeviceObject\n");
395 WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
396 goto hookworkitemdone;
397 }
398
399 KeInitializeEvent(&Event, NotificationEvent, FALSE);
400
401 NewIrp = IoBuildDeviceIoControlRequest(
402 IoControlCode,
403 WorkItemData->Target,
404 InputBuffer,
405 InputBufferLength,
406 NULL,
407 0,
408 TRUE,
409 &Event,
410 &IoStatus);
411
412 if (!NewIrp) {
413 DPRINT("IOCTL_INTERNAL_(device)_CONNECT: "
414 "Can't allocate IRP\n");
415 WorkItemData->Irp->IoStatus.Status =
416 STATUS_INSUFFICIENT_RESOURCES;
417 goto hookworkitemdone;
418 }
419
420 #if 0
421 Status = IoCallDriver(
422 WorkItemData->Target,
423 NewIrp);
424
425 if (STATUS_PENDING == Status)
426 KeWaitForSingleObject(&Event,
427 Executive,
428 KernelMode,
429 FALSE,
430 NULL);
431 #endif
432
433 if (IsKbd) {
434 /* Call the hooked initialization if it exists */
435 if (DevExt->KeyboardHook.InitializationRoutine) {
436 Status = DevExt->KeyboardHook.InitializationRoutine(
437 DevExt->KeyboardHook.Context,
438 DevExt,
439 I8042SynchReadPort,
440 I8042SynchWritePortKbd,
441 FALSE);
442 if (!NT_SUCCESS(Status)) {
443 WorkItemData->Irp->IoStatus.Status = Status;
444 goto hookworkitemdone;
445 }
446 }
447 /* TODO: Now would be the right time to enable the interrupt */
448
449 DevExt->KeyboardClaimed = TRUE;
450 } else {
451 /* Mouse doesn't have this, but we need to send a
452 * reset to start the detection.
453 */
454 KIRQL Irql;
455
456 Irql = KeAcquireInterruptSpinLock(
457 DevExt->HighestDIRQLInterrupt);
458
459 I8042Write(DevExt, I8042_CTRL_PORT, 0xD4);
460 I8042Write(DevExt, I8042_DATA_PORT, 0xFF);
461
462 KeReleaseInterruptSpinLock(DevExt->HighestDIRQLInterrupt, Irql);
463 }
464
465 WorkItemData->Irp->IoStatus.Status = STATUS_SUCCESS;
466
467 hookworkitemdone:
468 WorkItemData->Irp->IoStatus.Information = 0;
469 IoCompleteRequest(WorkItemData->Irp, IO_NO_INCREMENT);
470
471 IoFreeWorkItem(WorkItemData->WorkItem);
472 ExFreePool(WorkItemData);
473 DPRINT("HookWorkItem done\n");
474 }
475
476 static VOID STDCALL I8042StartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
477 {
478 if (!I8042StartIoKbd(DeviceObject, Irp)) {
479 DPRINT1("Unhandled StartIo!\n");
480 }
481 }
482
483 static NTSTATUS STDCALL I8042InternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
484 {
485 NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
486 PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
487
488 DPRINT("InternalDeviceControl\n");
489
490 switch (FdoDevExt->Type) {
491 case Keyboard:
492 Status = I8042InternalDeviceControlKbd(DeviceObject, Irp);
493 break;
494 case Mouse:
495 Status = I8042InternalDeviceControlMouse(DeviceObject, Irp);
496 break;
497 }
498
499 if (Status == STATUS_INVALID_DEVICE_REQUEST) {
500 DPRINT1("Invalid internal device request!\n");
501 }
502
503 if (Status != STATUS_PENDING)
504 IoCompleteRequest(Irp, IO_NO_INCREMENT);
505
506 return Status;
507 }
508
509 static NTSTATUS STDCALL I8042CreateDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
510 {
511 NTSTATUS Status;
512
513 DPRINT ("I8042CreateDispatch\n");
514
515 Status = STATUS_SUCCESS;
516
517 Irp->IoStatus.Status = Status;
518 Irp->IoStatus.Information = 0;
519 IoCompleteRequest(Irp, IO_NO_INCREMENT);
520
521 return Status;
522 }
523
524 static NTSTATUS STDCALL I8042BasicDetect(PDEVICE_EXTENSION DevExt)
525 {
526 NTSTATUS Status;
527 UCHAR Value = 0;
528 ULONG Counter;
529
530 DevExt->MouseExists = FALSE;
531 DevExt->KeyboardExists = FALSE;
532
533 I8042Flush();
534
535 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_SELF_TEST)) {
536 DPRINT1("Writing KBD_SELF_TEST command failed\n");
537 return STATUS_IO_TIMEOUT;
538 }
539
540 // Wait longer?
541 Counter = 3;
542 do {
543 Status = I8042ReadDataWait(DevExt, &Value);
544 } while ((Counter--) && (STATUS_IO_TIMEOUT == Status));
545
546 if (!NT_SUCCESS(Status)) {
547 DPRINT1("Failed to read KBD_SELF_TEST response, status 0x%x\n",
548 Status);
549 return Status;
550 }
551
552 if (Value != 0x55) {
553 DPRINT1("Got %x instead of 55\n", Value);
554 return STATUS_IO_DEVICE_ERROR;
555 }
556
557 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
558 DPRINT1("Can't read i8042 mode\n");
559 return FALSE;
560 }
561
562 Status = I8042ReadDataWait(DevExt, &Value);
563 if (!NT_SUCCESS(Status)) {
564 DPRINT1("No response after read i8042 mode\n");
565 return FALSE;
566 }
567
568 Value |= CCB_KBD_DISAB | CCB_MOUSE_DISAB; /* disable keyboard/mouse */
569 Value &= ~(CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB);
570 /* don't enable keyboard and mouse interrupts */
571
572 if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
573 DPRINT1("Can't set i8042 mode\n");
574 return FALSE;
575 }
576
577 if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
578 DPRINT1("Can't send i8042 mode\n");
579 return FALSE;
580 }
581
582 /*
583 * We used to send a KBD_LINE_TEST command here, but on at least HP
584 * Pavilion notebooks the response to that command was incorrect.
585 * So now we just assume that a keyboard is attached.
586 */
587 DevExt->KeyboardExists = TRUE;
588
589 if (I8042Write(DevExt, I8042_CTRL_PORT, MOUSE_LINE_TEST))
590 {
591 Status = I8042ReadDataWait(DevExt, &Value);
592 if (NT_SUCCESS(Status) && Value == 0)
593 DevExt->MouseExists = TRUE;
594 }
595
596 return STATUS_SUCCESS;
597 }
598
599 static NTSTATUS STDCALL I8042Initialize(PDEVICE_EXTENSION DevExt)
600 {
601 NTSTATUS Status;
602
603 Status = I8042BasicDetect(DevExt);
604 if (!NT_SUCCESS(Status)) {
605 DPRINT1("Basic keyboard detection failed: %x\n", Status);
606 return Status;
607 }
608
609 if (DevExt->MouseExists) {
610 DPRINT("Aux port detected\n");
611 DevExt->MouseExists = I8042DetectMouse(DevExt);
612 }
613
614 if (!DevExt->KeyboardExists) {
615 DPRINT("Keyboard port not detected\n");
616 if (DevExt->Settings.Headless)
617 /* Act as if it exists regardless */
618 DevExt->KeyboardExists = TRUE;
619 } else {
620 DPRINT("Keyboard port detected\n");
621 DevExt->KeyboardExists = I8042DetectKeyboard(DevExt);
622 }
623
624 if (DevExt->KeyboardExists) {
625 DPRINT("Keyboard detected\n");
626 I8042KeyboardEnable(DevExt);
627 I8042KeyboardEnableInterrupt(DevExt);
628 }
629
630 if (DevExt->MouseExists) {
631 DPRINT("Mouse detected\n");
632 I8042MouseEnable(DevExt);
633 }
634
635 return STATUS_SUCCESS;
636 }
637
638 static NTSTATUS
639 AddRegistryEntry(
640 IN PCWSTR PortTypeName,
641 IN PUNICODE_STRING DeviceName,
642 IN PCWSTR RegistryPath)
643 {
644 UNICODE_STRING PathU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
645 OBJECT_ATTRIBUTES ObjectAttributes;
646 HANDLE hDeviceMapKey = (HANDLE)-1;
647 HANDLE hPortKey = (HANDLE)-1;
648 UNICODE_STRING PortTypeNameU;
649 NTSTATUS Status;
650
651 InitializeObjectAttributes(&ObjectAttributes, &PathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
652 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
653 if (!NT_SUCCESS(Status))
654 {
655 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
656 goto cleanup;
657 }
658
659 RtlInitUnicodeString(&PortTypeNameU, PortTypeName);
660 InitializeObjectAttributes(&ObjectAttributes, &PortTypeNameU, OBJ_KERNEL_HANDLE, hDeviceMapKey, NULL);
661 Status = ZwCreateKey(&hPortKey, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL);
662 if (!NT_SUCCESS(Status))
663 {
664 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
665 goto cleanup;
666 }
667
668 Status = ZwSetValueKey(hPortKey, DeviceName, 0, REG_SZ, (PVOID)RegistryPath, wcslen(RegistryPath) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
669 if (!NT_SUCCESS(Status))
670 {
671 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
672 goto cleanup;
673 }
674
675 Status = STATUS_SUCCESS;
676
677 cleanup:
678 if (hDeviceMapKey != (HANDLE)-1)
679 ZwClose(hDeviceMapKey);
680 if (hPortKey != (HANDLE)-1)
681 ZwClose(hPortKey);
682 return Status;
683 }
684
685 static NTSTATUS STDCALL I8042AddDevice(PDRIVER_OBJECT DriverObject,
686 PDEVICE_OBJECT Pdo)
687 {
688 UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardPort8042");
689 UNICODE_STRING MouseName = RTL_CONSTANT_STRING(L"\\Device\\PointerPort8042");
690 ULONG MappedIrqKeyboard = 0, MappedIrqMouse = 0;
691 KIRQL DirqlKeyboard = 0;
692 KIRQL DirqlMouse = 0;
693 KIRQL DirqlMax;
694 KAFFINITY Affinity;
695 NTSTATUS Status;
696 PDEVICE_EXTENSION DevExt;
697 PFDO_DEVICE_EXTENSION FdoDevExt;
698 PDEVICE_OBJECT Fdo;
699 static BOOLEAN AlreadyAdded = FALSE;
700
701 DPRINT("I8042AddDevice\n");
702
703 /* HACK! */
704 if (AlreadyAdded)
705 return STATUS_UNSUCCESSFUL;
706 AlreadyAdded = TRUE;
707
708 Status = IoCreateDevice(DriverObject,
709 sizeof(DEVICE_EXTENSION),
710 NULL,
711 FILE_DEVICE_8042_PORT,
712 FILE_DEVICE_SECURE_OPEN,
713 TRUE,
714 &Fdo);
715
716 if (!NT_SUCCESS(Status))
717 return Status;
718
719 IoAttachDeviceToDeviceStack(Fdo, Pdo);
720
721 DevExt = Fdo->DeviceExtension;
722
723 RtlZeroMemory(DevExt, sizeof(DEVICE_EXTENSION));
724
725 I8042ReadRegistry(DriverObject, DevExt);
726
727 KeInitializeSpinLock(&DevExt->SpinLock);
728 InitializeListHead(&DevExt->BusDevices);
729
730 KeInitializeDpc(&DevExt->DpcKbd,
731 I8042DpcRoutineKbd,
732 DevExt);
733
734 KeInitializeDpc(&DevExt->DpcMouse,
735 I8042DpcRoutineMouse,
736 DevExt);
737
738 KeInitializeDpc(&DevExt->DpcMouseTimeout,
739 I8042DpcRoutineMouseTimeout,
740 DevExt);
741
742 KeInitializeTimer(&DevExt->TimerMouseTimeout);
743
744 Status = I8042Initialize(DevExt);
745 if (!NT_SUCCESS(STATUS_SUCCESS)) {
746 DPRINT1("Initialization failure: %x\n", Status);
747 return Status;
748 }
749
750 if (DevExt->KeyboardExists) {
751 MappedIrqKeyboard = HalGetInterruptVector(Internal,
752 0,
753 0,
754 KEYBOARD_IRQ,
755 &DirqlKeyboard,
756 &Affinity);
757
758 Status = IoCreateDevice(DriverObject,
759 sizeof(FDO_DEVICE_EXTENSION),
760 &DeviceName,
761 FILE_DEVICE_8042_PORT,
762 FILE_DEVICE_SECURE_OPEN,
763 TRUE,
764 &Fdo);
765
766 if (NT_SUCCESS(Status))
767 {
768 AddRegistryEntry(L"KeyboardPort", &DeviceName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt");
769 FdoDevExt = Fdo->DeviceExtension;
770
771 RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION));
772
773 FdoDevExt->PortDevExt = DevExt;
774 FdoDevExt->Type = Keyboard;
775 FdoDevExt->DeviceObject = Fdo;
776
777 Fdo->Flags |= DO_BUFFERED_IO;
778
779 DevExt->DebugWorkItem = IoAllocateWorkItem(Fdo);
780 DevExt->KeyboardObject = Fdo;
781
782 DevExt->KeyboardBuffer = ExAllocatePoolWithTag(
783 NonPagedPool,
784 DevExt->KeyboardAttributes.InputDataQueueLength *
785 sizeof(KEYBOARD_INPUT_DATA),
786 TAG_I8042);
787
788 if (!DevExt->KeyboardBuffer) {
789 DPRINT1("No memory for keyboardbuffer\n");
790 return STATUS_INSUFFICIENT_RESOURCES;
791 }
792
793 InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices);
794 }
795 else
796 DevExt->KeyboardExists = FALSE;
797 }
798
799 if (DevExt->MouseExists) {
800 MappedIrqMouse = HalGetInterruptVector(Internal,
801 0,
802 0,
803 MOUSE_IRQ,
804 &DirqlMouse,
805 &Affinity);
806
807 Status = IoCreateDevice(DriverObject,
808 sizeof(FDO_DEVICE_EXTENSION),
809 &MouseName,
810 FILE_DEVICE_8042_PORT,
811 FILE_DEVICE_SECURE_OPEN,
812 TRUE,
813 &Fdo);
814
815 if (NT_SUCCESS(Status))
816 {
817 AddRegistryEntry(L"PointerPort", &MouseName, L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\i8042prt");
818 FdoDevExt = Fdo->DeviceExtension;
819
820 RtlZeroMemory(FdoDevExt, sizeof(FDO_DEVICE_EXTENSION));
821
822 FdoDevExt->PortDevExt = DevExt;
823 FdoDevExt->Type = Mouse;
824 FdoDevExt->DeviceObject = Fdo;
825
826 Fdo->Flags |= DO_BUFFERED_IO;
827 DevExt->MouseObject = Fdo;
828
829 DevExt->MouseBuffer = ExAllocatePoolWithTag(
830 NonPagedPool,
831 DevExt->MouseAttributes.InputDataQueueLength *
832 sizeof(MOUSE_INPUT_DATA),
833 TAG_I8042);
834
835 if (!DevExt->MouseBuffer) {
836 ExFreePoolWithTag(DevExt->KeyboardBuffer, TAG_I8042);
837 DPRINT1("No memory for mouse buffer\n");
838 return STATUS_INSUFFICIENT_RESOURCES;
839 }
840
841 InsertTailList(&DevExt->BusDevices, &FdoDevExt->BusDevices);
842 }
843 else
844 DevExt->MouseExists = FALSE;
845 }
846
847 if (DirqlKeyboard > DirqlMouse)
848 DirqlMax = DirqlKeyboard;
849 else
850 DirqlMax = DirqlMouse;
851
852 if (DevExt->KeyboardExists) {
853 Status = IoConnectInterrupt(&DevExt->KeyboardInterruptObject,
854 I8042InterruptServiceKbd,
855 (PVOID)DevExt,
856 &DevExt->SpinLock,
857 MappedIrqKeyboard,
858 DirqlKeyboard,
859 DirqlMax,
860 LevelSensitive,
861 FALSE,
862 Affinity,
863 FALSE);
864
865 DPRINT("Keyboard Irq Status: %x\n", Status);
866 }
867
868 if (DevExt->MouseExists) {
869 Status = IoConnectInterrupt(&DevExt->MouseInterruptObject,
870 I8042InterruptServiceMouse,
871 (PVOID)DevExt,
872 &DevExt->SpinLock,
873 MappedIrqMouse,
874 DirqlMouse,
875 DirqlMax,
876 LevelSensitive,
877 FALSE,
878 Affinity,
879 FALSE);
880
881 DPRINT("Mouse Irq Status: %x\n", Status);
882 }
883
884 if (DirqlKeyboard > DirqlMouse)
885 DevExt->HighestDIRQLInterrupt = DevExt->KeyboardInterruptObject;
886 else
887 DevExt->HighestDIRQLInterrupt = DevExt->MouseInterruptObject;
888
889 DPRINT("I8042AddDevice done\n");
890
891 return(STATUS_SUCCESS);
892 }
893
894 NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject,
895 PUNICODE_STRING RegistryPath)
896 /*
897 * FUNCTION: Module entry point
898 */
899 {
900 DPRINT("I8042 Driver 0.0.1\n");
901
902 I8042RegistryPath.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters");
903 I8042RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
904 I8042RegistryPath.MaximumLength,
905 TAG_I8042);
906 if (I8042RegistryPath.Buffer == NULL) {
907
908 return STATUS_INSUFFICIENT_RESOURCES;
909 }
910
911 RtlCopyUnicodeString(&I8042RegistryPath, RegistryPath);
912 RtlAppendUnicodeToString(&I8042RegistryPath, L"\\Parameters");
913 I8042RegistryPath.Buffer[I8042RegistryPath.Length / sizeof(WCHAR)] = 0;
914
915
916
917 DriverObject->MajorFunction[IRP_MJ_CREATE] = I8042CreateDispatch;
918 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
919 I8042InternalDeviceControl;
920
921 DriverObject->DriverStartIo = I8042StartIo;
922 DriverObject->DriverExtension->AddDevice = I8042AddDevice;
923
924 return(STATUS_SUCCESS);
925 }