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