i8042prt driver by tinus.
[reactos.git] / reactos / drivers / input / mouclass / mouclass.c
1 /*
2
3 ** Mouse class driver 0.0.1
4 ** Written by Jason Filby (jasonfilby@yahoo.com)
5 ** For ReactOS (www.reactos.com)
6
7 ** The class driver between win32k and the various mouse port drivers
8
9 ** TODO: Change interface to win32k to a callback instead of ReadFile IO
10 Add support for multiple port devices
11
12 */
13
14 #include <ddk/ntddk.h>
15 #include <ddk/ntddmou.h>
16 #include <rosrtl/string.h>
17 #include "mouclass.h"
18
19 #define NDEBUG
20 #include <debug.h>
21
22 BOOLEAN MouseClassCallBack(
23 PDEVICE_OBJECT ClassDeviceObject, PMOUSE_INPUT_DATA MouseDataStart,
24 PMOUSE_INPUT_DATA MouseDataEnd, PULONG ConsumedCount)
25 {
26 PDEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
27 PIRP Irp;
28 KIRQL OldIrql;
29 PIO_STACK_LOCATION Stack;
30 ULONG InputCount = MouseDataEnd - MouseDataStart;
31 ULONG ReadSize;
32
33 DPRINT("Entering MouseClassCallBack\n");
34 /* A filter driver might have consumed all the data already; I'm
35 * not sure if they are supposed to move the packets when they
36 * consume them though.
37 */
38 if (ClassDeviceExtension->ReadIsPending == TRUE &&
39 InputCount)
40 {
41 Irp = ClassDeviceObject->CurrentIrp;
42 ClassDeviceObject->CurrentIrp = NULL;
43 Stack = IoGetCurrentIrpStackLocation(Irp);
44
45 /* A read request is waiting for input, so go straight to it */
46 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, MouseDataStart,
47 sizeof(MOUSE_INPUT_DATA));
48
49 /* Go to next packet and complete this request with STATUS_SUCCESS */
50 Irp->IoStatus.Status = STATUS_SUCCESS;
51 Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
52 Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
53
54 IoStartNextPacket(ClassDeviceObject, FALSE);
55 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
56 ClassDeviceExtension->ReadIsPending = FALSE;
57
58 /* Skip the packet we just sent away */
59 MouseDataStart++;
60 (*ConsumedCount)++;
61 InputCount--;
62 }
63
64 /* If we have data from the port driver and a higher service to send the data to */
65 if (InputCount != 0)
66 {
67 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
68
69 if (ClassDeviceExtension->InputCount + InputCount > MOUSE_BUFFER_SIZE)
70 {
71 ReadSize = MOUSE_BUFFER_SIZE - ClassDeviceExtension->InputCount;
72 } else {
73 ReadSize = InputCount;
74 }
75
76 /*
77 * FIXME: If we exceed the buffer, mouse data gets thrown away.. better
78 * solution?
79 */
80
81 /*
82 * Move the mouse input data from the port data queue to our class data
83 * queue.
84 */
85 RtlMoveMemory(ClassDeviceExtension->PortData, (PCHAR)MouseDataStart,
86 sizeof(MOUSE_INPUT_DATA) * ReadSize);
87
88 /* Move the pointer and counter up */
89 ClassDeviceExtension->PortData += ReadSize;
90 ClassDeviceExtension->InputCount += ReadSize;
91
92 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
93 (*ConsumedCount) += ReadSize;
94 } else {
95 DPRINT("MouseClassCallBack() entered, InputCount = %d - DOING NOTHING\n", *InputCount);
96 }
97
98 DPRINT("Leaving MouseClassCallBack\n");
99 return TRUE;
100 }
101
102 NTSTATUS ConnectMousePortDriver(PDEVICE_OBJECT ClassDeviceObject)
103 {
104 PDEVICE_OBJECT PortDeviceObject = NULL;
105 PFILE_OBJECT FileObject = NULL;
106 NTSTATUS status;
107 UNICODE_STRING PortName = ROS_STRING_INITIALIZER(L"\\Device\\PointerClass0");
108 IO_STATUS_BLOCK ioStatus;
109 KEVENT event;
110 PIRP irp;
111 CLASS_INFORMATION ClassInformation;
112 PDEVICE_EXTENSION DeviceExtension = ClassDeviceObject->DeviceExtension;
113
114 // Get the port driver's DeviceObject
115 // FIXME: The name might change.. find a way to be more dynamic?
116
117 status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
118
119 if(status != STATUS_SUCCESS)
120 {
121 DPRINT("MOUCLASS: Could not connect to mouse port driver\n");
122 DPRINT("Status: %x\n", status);
123 return status;
124 }
125
126 DeviceExtension->PortDeviceObject = PortDeviceObject;
127 DeviceExtension->PortData = ExAllocatePool(NonPagedPool, MOUSE_BUFFER_SIZE * sizeof(MOUSE_INPUT_DATA));
128 DeviceExtension->InputCount = 0;
129 DeviceExtension->ReadIsPending = FALSE;
130 DeviceExtension->WorkItem = NULL;
131 KeInitializeSpinLock(&(DeviceExtension->SpinLock));
132 DeviceExtension->PassiveCallbackQueued = FALSE;
133
134 // Connect our callback to the port driver
135
136 KeInitializeEvent(&event, NotificationEvent, FALSE);
137
138 ClassInformation.DeviceObject = ClassDeviceObject;
139 ClassInformation.CallBack = MouseClassCallBack;
140
141 irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_MOUSE_CONNECT,
142 PortDeviceObject, &ClassInformation, sizeof(CLASS_INFORMATION), NULL, 0, TRUE, &event, &ioStatus);
143
144 status = IoCallDriver(DeviceExtension->PortDeviceObject, irp);
145
146 if (status == STATUS_PENDING) {
147 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
148 } else {
149 ioStatus.Status = status;
150 }
151
152 return ioStatus.Status;
153 }
154
155 NTSTATUS STDCALL MouseClassDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
156 {
157 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
158 NTSTATUS Status;
159
160 switch (Stack->MajorFunction)
161 {
162 case IRP_MJ_CREATE:
163 Status = STATUS_SUCCESS;
164 break;
165
166 case IRP_MJ_CLOSE:
167 Status = STATUS_SUCCESS;
168 break;
169
170 case IRP_MJ_READ:
171 if (Stack->Parameters.Read.Length < sizeof(MOUSE_INPUT_DATA))
172 {
173 Status = STATUS_BUFFER_TOO_SMALL;
174 break;
175 }
176 IoMarkIrpPending(Irp);
177 IoStartPacket(DeviceObject, Irp, NULL, NULL);
178 return STATUS_PENDING;
179
180 default:
181 DPRINT1("NOT IMPLEMENTED\n");
182 Status = STATUS_NOT_IMPLEMENTED;
183 break;
184 }
185
186 Irp->IoStatus.Status = Status;
187 Irp->IoStatus.Information = 0;
188 IoCompleteRequest(Irp, IO_NO_INCREMENT);
189
190 return Status;
191 }
192
193 VOID STDCALL
194 MouseClassStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
195 {
196 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
197 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
198
199 if (DeviceExtension->InputCount > 0)
200 {
201 KIRQL oldIrql;
202
203 KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
204
205 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
206 DeviceExtension->PortData - DeviceExtension->InputCount,
207 sizeof(MOUSE_INPUT_DATA));
208
209 if (DeviceExtension->InputCount > 1)
210 {
211 RtlMoveMemory(
212 DeviceExtension->PortData - DeviceExtension->InputCount,
213 DeviceExtension->PortData - DeviceExtension->InputCount + 1,
214 (DeviceExtension->InputCount - 1) * sizeof(MOUSE_INPUT_DATA));
215 }
216 DeviceExtension->PortData--;
217 DeviceExtension->InputCount--;
218 DeviceExtension->ReadIsPending = FALSE;
219
220 /* Go to next packet and complete this request with STATUS_SUCCESS */
221 Irp->IoStatus.Status = STATUS_SUCCESS;
222 Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
223 Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
224 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
225
226 IoStartNextPacket(DeviceObject, FALSE);
227 KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
228 } else {
229 DeviceExtension->ReadIsPending = TRUE;
230 }
231 }
232
233 NTSTATUS STDCALL
234 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
235 {
236 PDEVICE_OBJECT DeviceObject;
237 UNICODE_STRING DeviceName = ROS_STRING_INITIALIZER(L"\\Device\\Mouse");
238 UNICODE_STRING SymlinkName = ROS_STRING_INITIALIZER(L"\\??\\Mouse");
239 NTSTATUS Status;
240
241 DriverObject->MajorFunction[IRP_MJ_CREATE] = MouseClassDispatch;
242 DriverObject->MajorFunction[IRP_MJ_CLOSE] = MouseClassDispatch;
243 DriverObject->MajorFunction[IRP_MJ_READ] = MouseClassDispatch;
244 DriverObject->DriverStartIo = MouseClassStartIo;
245
246 Status = IoCreateDevice(DriverObject,
247 sizeof(DEVICE_EXTENSION),
248 &DeviceName,
249 FILE_DEVICE_MOUSE,
250 0,
251 TRUE,
252 &DeviceObject);
253 if (!NT_SUCCESS(Status))
254 {
255 return(Status);
256 }
257
258 DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
259
260 Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
261 if (!NT_SUCCESS(Status))
262 {
263 IoDeleteDevice(DeviceObject);
264 return Status;
265 }
266
267 Status = ConnectMousePortDriver(DeviceObject);
268 if (!NT_SUCCESS(Status))
269 {
270 IoDeleteSymbolicLink(&SymlinkName);
271 IoDeleteDevice(DeviceObject);
272 return Status;
273 }
274
275 return STATUS_SUCCESS;
276 }