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