xref: /reactos/drivers/input/i8042prt/i8042prt.c (revision 845faec4)
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     i8042InitializeHwHacks();
564 
565 	return STATUS_SUCCESS;
566 }
567