xref: /reactos/drivers/input/i8042prt/i8042prt.c (revision 7b1049c8)
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 }
537