Serial mouse driver rewrite by Filip Navara
[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 "mouclass.h"
17
18 #define NDEBUG
19 #include <debug.h>
20
21 BOOLEAN AlreadyOpened = FALSE;
22
23 VOID MouseClassPassiveCallback(PDEVICE_OBJECT ClassDeviceObject, PVOID Context)
24 {
25 PDEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
26 MOUSE_INPUT_DATA PortData[MOUSE_BUFFER_SIZE];
27 ULONG InputCount;
28 KIRQL OldIrql;
29
30 assert(NULL != ClassDeviceExtension->GDIInformation.CallBack);
31 KeAcquireSpinLock(&(ClassDeviceExtension->SpinLock), &OldIrql);
32 DPRINT("Entering MouseClassPassiveCallback\n");
33 while (0 != ClassDeviceExtension->InputCount) {
34 ClassDeviceExtension->PortData -= ClassDeviceExtension->InputCount;
35 RtlMoveMemory(PortData, ClassDeviceExtension->PortData,
36 ClassDeviceExtension->InputCount * sizeof(MOUSE_INPUT_DATA));
37 InputCount = ClassDeviceExtension->InputCount;
38 ClassDeviceExtension->InputCount = 0;
39 KeReleaseSpinLock(&(ClassDeviceExtension->SpinLock), OldIrql);
40
41 DPRINT("MouseClassPassiveCallBack() Calling GDI callback at %p\n",
42 ClassDeviceExtension->GDIInformation.CallBack);
43 /* We're jumping through hoops to get to run at PASSIVE_LEVEL, let's make
44 sure we succeeded */
45 ASSERT_IRQL(PASSIVE_LEVEL);
46 (*(PGDI_SERVICE_CALLBACK_ROUTINE)ClassDeviceExtension->GDIInformation.CallBack)
47 (PortData, InputCount);
48
49 KeAcquireSpinLock(&(ClassDeviceExtension->SpinLock), &OldIrql);
50 }
51
52 ClassDeviceExtension->PassiveCallbackQueued = FALSE;
53 DPRINT("Leaving MouseClassPassiveCallback\n");
54 KeReleaseSpinLock(&(ClassDeviceExtension->SpinLock), OldIrql);
55 }
56
57 BOOLEAN MouseClassCallBack(PDEVICE_OBJECT ClassDeviceObject, PMOUSE_INPUT_DATA MouseDataStart,
58 PMOUSE_INPUT_DATA MouseDataEnd, PULONG InputCount)
59 {
60 PDEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
61 PIRP Irp;
62 ULONG ReadSize;
63 PIO_STACK_LOCATION Stack;
64 KIRQL OldIrql;
65
66 // In classical NT, you would take the input data and pipe it through the IO system, for the GDI to read.
67 // In ReactOS, however, we use a GDI callback for increased mouse responsiveness. The reason we don't
68 // simply call from the port driver is so that our mouse class driver can support NT mouse port drivers.
69
70 DPRINT("Entering MouseClassCallBack\n");
71 /* if(ClassDeviceExtension->ReadIsPending == TRUE)
72 {
73 Irp = ClassDeviceObject->CurrentIrp;
74 ClassDeviceObject->CurrentIrp = NULL;
75 Stack = IoGetCurrentIrpStackLocation(Irp);
76
77 ReadSize = sizeof(MOUSE_INPUT_DATA) * (*InputCount);
78
79 // A read request is waiting for input, so go straight to it
80 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, (PCHAR)MouseDataStart, ReadSize);
81
82 // Go to next packet and complete this request with STATUS_SUCCESS
83 Irp->IoStatus.Status = STATUS_SUCCESS;
84 Irp->IoStatus.Information = ReadSize;
85 Stack->Parameters.Read.Length = ReadSize;
86
87 IoStartNextPacket(ClassDeviceObject, FALSE);
88 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
89 ClassDeviceExtension->ReadIsPending = FALSE;
90 } */
91
92 // If we have data from the port driver and a higher service to send the data to
93 if((*InputCount>0) && (*(PGDI_SERVICE_CALLBACK_ROUTINE)ClassDeviceExtension->GDIInformation.CallBack != NULL))
94 {
95 KeAcquireSpinLock(&(ClassDeviceExtension->SpinLock), &OldIrql);
96
97 if(ClassDeviceExtension->InputCount + *InputCount > MOUSE_BUFFER_SIZE)
98 {
99 ReadSize = MOUSE_BUFFER_SIZE - ClassDeviceExtension->InputCount;
100 } else {
101 ReadSize = *InputCount;
102 }
103
104 // FIXME: If we exceed the buffer, mouse data gets thrown away.. better solution?
105
106
107 // Move the mouse input data from the port data queue to our class data queue
108 RtlMoveMemory(ClassDeviceExtension->PortData, (PCHAR)MouseDataStart,
109 sizeof(MOUSE_INPUT_DATA) * ReadSize);
110
111 // Move the pointer and counter up
112 ClassDeviceExtension->PortData += ReadSize;
113 ClassDeviceExtension->InputCount += ReadSize;
114
115 if(*(PGDI_SERVICE_CALLBACK_ROUTINE)ClassDeviceExtension->GDIInformation.CallBack != NULL) {
116 if (! ClassDeviceExtension->PassiveCallbackQueued) {
117 if (NULL == ClassDeviceExtension->WorkItem) {
118 ClassDeviceExtension->WorkItem = IoAllocateWorkItem(ClassDeviceObject);
119 }
120 if (NULL != ClassDeviceExtension->WorkItem) {
121 DPRINT("Queueing workitem\n");
122 IoQueueWorkItem(ClassDeviceExtension->WorkItem, MouseClassPassiveCallback, CriticalWorkQueue, NULL);
123 ClassDeviceExtension->PassiveCallbackQueued = TRUE;
124 }
125 }
126 } else {
127 DPRINT("MouseClassCallBack() NO GDI callback installed\n");
128 }
129 KeReleaseSpinLock(&(ClassDeviceExtension->SpinLock), OldIrql);
130 } else {
131 DPRINT("MouseClassCallBack() entered, InputCount = %d - DOING NOTHING\n", *InputCount);
132 }
133
134 DPRINT("Leaving MouseClassCallBack\n");
135 return TRUE;
136 }
137
138 NTSTATUS ConnectMousePortDriver(PDEVICE_OBJECT ClassDeviceObject)
139 {
140 PDEVICE_OBJECT PortDeviceObject = NULL;
141 PFILE_OBJECT FileObject = NULL;
142 NTSTATUS status;
143 UNICODE_STRING PortName = UNICODE_STRING_INITIALIZER(L"\\Device\\PointerClass0");
144 IO_STATUS_BLOCK ioStatus;
145 KEVENT event;
146 PIRP irp;
147 CLASS_INFORMATION ClassInformation;
148 PDEVICE_EXTENSION DeviceExtension = ClassDeviceObject->DeviceExtension;
149
150 DeviceExtension->GDIInformation.CallBack = NULL;
151
152 // Get the port driver's DeviceObject
153 // FIXME: The name might change.. find a way to be more dynamic?
154
155 status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
156
157 if(status != STATUS_SUCCESS)
158 {
159 DPRINT1("MOUCLASS: Could not connect to mouse port driver\n");
160 return status;
161 }
162
163 DeviceExtension->PortDeviceObject = PortDeviceObject;
164 DeviceExtension->PortData = ExAllocatePool(NonPagedPool, MOUSE_BUFFER_SIZE * sizeof(MOUSE_INPUT_DATA));
165 DeviceExtension->InputCount = 0;
166 DeviceExtension->ReadIsPending = FALSE;
167 DeviceExtension->WorkItem = NULL;
168 KeInitializeSpinLock(&(DeviceExtension->SpinLock));
169 DeviceExtension->PassiveCallbackQueued = FALSE;
170
171 // Connect our callback to the port driver
172
173 KeInitializeEvent(&event, NotificationEvent, FALSE);
174
175 ClassInformation.DeviceObject = ClassDeviceObject;
176 ClassInformation.CallBack = MouseClassCallBack;
177
178 irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_MOUSE_CONNECT,
179 PortDeviceObject, &ClassInformation, sizeof(CLASS_INFORMATION), NULL, 0, TRUE, &event, &ioStatus);
180
181 status = IoCallDriver(DeviceExtension->PortDeviceObject, irp);
182
183 if (status == STATUS_PENDING) {
184 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
185 } else {
186 ioStatus.Status = status;
187 }
188
189 return ioStatus.Status;
190 }
191
192 NTSTATUS STDCALL_FUNC MouseClassDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
193 {
194 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
195 NTSTATUS Status;
196
197 switch (Stack->MajorFunction)
198 {
199 case IRP_MJ_CREATE:
200 if (AlreadyOpened == TRUE)
201 {
202 Status = STATUS_SUCCESS;
203 }
204 else
205 {
206 Status = STATUS_SUCCESS;
207 AlreadyOpened = TRUE;
208 }
209 break;
210
211 case IRP_MJ_CLOSE:
212 Status = STATUS_SUCCESS;
213 break;
214
215 case IRP_MJ_READ:
216
217 if (Stack->Parameters.Read.Length == 0) {
218 Status = STATUS_SUCCESS;
219 } else {
220 Status = STATUS_PENDING;
221 }
222 break;
223
224 default:
225 DPRINT1("NOT IMPLEMENTED\n");
226 Status = STATUS_NOT_IMPLEMENTED;
227 break;
228 }
229
230 Irp->IoStatus.Status = Status;
231 Irp->IoStatus.Information = 0;
232 if (Status==STATUS_PENDING)
233 {
234 IoMarkIrpPending(Irp);
235 IoStartPacket(DeviceObject, Irp, NULL, NULL);
236 } else {
237 IoCompleteRequest(Irp, IO_NO_INCREMENT);
238 }
239 return(Status);
240 }
241
242 VOID MouseClassStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
243 {
244 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
245 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
246 ULONG ReadSize;
247
248 if(DeviceExtension->InputCount>0)
249 {
250 // FIXME: We should not send too much input data.. depends on the max buffer size of the win32k
251 ReadSize = DeviceExtension->InputCount * sizeof(MOUSE_INPUT_DATA);
252
253 // Bring the PortData back to base so that it can be copied
254 DeviceExtension->PortData -= DeviceExtension->InputCount;
255 DeviceExtension->InputCount = 0;
256 DeviceExtension->ReadIsPending = FALSE;
257
258 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, (PCHAR)DeviceExtension->PortData, ReadSize);
259
260 // Go to next packet and complete this request with STATUS_SUCCESS
261 Irp->IoStatus.Status = STATUS_SUCCESS;
262
263 Irp->IoStatus.Information = ReadSize;
264 Stack->Parameters.Read.Length = ReadSize;
265
266 IoStartNextPacket(DeviceObject, FALSE);
267 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
268 } else {
269 DeviceExtension->ReadIsPending = TRUE;
270 }
271 }
272
273 NTSTATUS STDCALL_FUNC MouseClassInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
274 {
275 // Retrieve GDI's callback
276
277 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
278 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
279 NTSTATUS status;
280
281 switch(Stack->Parameters.DeviceIoControl.IoControlCode)
282 {
283 case IOCTL_INTERNAL_MOUSE_CONNECT:
284
285 DeviceExtension->GDIInformation =
286 *((PGDI_INFORMATION)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
287
288 DPRINT("MouseClassInternalDeviceControl() installed GDI callback at %p\n", DeviceExtension->GDIInformation.CallBack);
289
290 status = STATUS_SUCCESS;
291 break;
292
293 case IOCTL_INTERNAL_MOUSE_DISCONNECT:
294
295 DeviceExtension->GDIInformation.CallBack = NULL;
296
297 status = STATUS_SUCCESS;
298 break;
299
300 default:
301 status = STATUS_INVALID_DEVICE_REQUEST;
302 break;
303 }
304
305 Irp->IoStatus.Status = status;
306 if (status == STATUS_PENDING) {
307 IoMarkIrpPending(Irp);
308 IoStartPacket(DeviceObject, Irp, NULL, NULL);
309 } else {
310 IoCompleteRequest(Irp, IO_NO_INCREMENT);
311 }
312
313 return status;
314 }
315
316 NTSTATUS STDCALL
317 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
318 {
319 PDEVICE_OBJECT DeviceObject;
320 UNICODE_STRING DeviceName = UNICODE_STRING_INITIALIZER(L"\\Device\\MouseClass");
321 UNICODE_STRING SymlinkName = UNICODE_STRING_INITIALIZER(L"\\??\\MouseClass"); NTSTATUS Status;
322
323
324 DriverObject->MajorFunction[IRP_MJ_CREATE] = MouseClassDispatch;
325 // DriverObject->MajorFunction[IRP_MJ_CLOSE] = MouseClassDispatch;
326 // DriverObject->MajorFunction[IRP_MJ_READ] = MouseClassDispatch;
327 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MouseClassInternalDeviceControl; // to get GDI callback
328 // DriverObject->DriverStartIo = MouseClassStartIo;
329
330 Status = IoCreateDevice(DriverObject,
331 sizeof(DEVICE_EXTENSION),
332 &DeviceName,
333 FILE_DEVICE_MOUSE,
334 0,
335 TRUE,
336 &DeviceObject);
337 if (!NT_SUCCESS(Status))
338 {
339 return(Status);
340 }
341 DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
342
343 Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
344 if (!NT_SUCCESS(Status))
345 {
346 IoDeleteDevice(DeviceObject);
347 return(Status);
348 }
349
350 Status = ConnectMousePortDriver(DeviceObject);
351 if (!NT_SUCCESS(Status))
352 {
353 IoDeleteSymbolicLink(&SymlinkName);
354 IoDeleteDevice(DeviceObject);
355 return(Status);
356 }
357 return(STATUS_SUCCESS);
358 }