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