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