Changed DPRINT1 to DPRINT when connecting to mouse device.
[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 DPRINT("MOUCLASS: Could not connect to mouse port driver\n");
160 DPRINT("Status: %x\n", status);
161 return status;
162 }
163
164 DeviceExtension->PortDeviceObject = PortDeviceObject;
165 DeviceExtension->PortData = ExAllocatePool(NonPagedPool, MOUSE_BUFFER_SIZE * sizeof(MOUSE_INPUT_DATA));
166 DeviceExtension->InputCount = 0;
167 DeviceExtension->ReadIsPending = FALSE;
168 DeviceExtension->WorkItem = NULL;
169 KeInitializeSpinLock(&(DeviceExtension->SpinLock));
170 DeviceExtension->PassiveCallbackQueued = FALSE;
171
172 // Connect our callback to the port driver
173
174 KeInitializeEvent(&event, NotificationEvent, FALSE);
175
176 ClassInformation.DeviceObject = ClassDeviceObject;
177 ClassInformation.CallBack = MouseClassCallBack;
178
179 irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_MOUSE_CONNECT,
180 PortDeviceObject, &ClassInformation, sizeof(CLASS_INFORMATION), NULL, 0, TRUE, &event, &ioStatus);
181
182 status = IoCallDriver(DeviceExtension->PortDeviceObject, irp);
183
184 if (status == STATUS_PENDING) {
185 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
186 } else {
187 ioStatus.Status = status;
188 }
189
190 return ioStatus.Status;
191 }
192
193 NTSTATUS STDCALL_FUNC MouseClassDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
194 {
195 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
196 NTSTATUS Status;
197
198 switch (Stack->MajorFunction)
199 {
200 case IRP_MJ_CREATE:
201 if (AlreadyOpened == TRUE)
202 {
203 Status = STATUS_SUCCESS;
204 }
205 else
206 {
207 Status = STATUS_SUCCESS;
208 AlreadyOpened = TRUE;
209 }
210 break;
211
212 case IRP_MJ_CLOSE:
213 Status = STATUS_SUCCESS;
214 break;
215
216 case IRP_MJ_READ:
217
218 if (Stack->Parameters.Read.Length == 0) {
219 Status = STATUS_SUCCESS;
220 } else {
221 Status = STATUS_PENDING;
222 }
223 break;
224
225 default:
226 DPRINT1("NOT IMPLEMENTED\n");
227 Status = STATUS_NOT_IMPLEMENTED;
228 break;
229 }
230
231 Irp->IoStatus.Status = Status;
232 Irp->IoStatus.Information = 0;
233 if (Status==STATUS_PENDING)
234 {
235 IoMarkIrpPending(Irp);
236 IoStartPacket(DeviceObject, Irp, NULL, NULL);
237 } else {
238 IoCompleteRequest(Irp, IO_NO_INCREMENT);
239 }
240 return(Status);
241 }
242
243 VOID MouseClassStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
244 {
245 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
246 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
247 ULONG ReadSize;
248
249 if(DeviceExtension->InputCount>0)
250 {
251 // FIXME: We should not send too much input data.. depends on the max buffer size of the win32k
252 ReadSize = DeviceExtension->InputCount * sizeof(MOUSE_INPUT_DATA);
253
254 // Bring the PortData back to base so that it can be copied
255 DeviceExtension->PortData -= DeviceExtension->InputCount;
256 DeviceExtension->InputCount = 0;
257 DeviceExtension->ReadIsPending = FALSE;
258
259 RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, (PCHAR)DeviceExtension->PortData, ReadSize);
260
261 // Go to next packet and complete this request with STATUS_SUCCESS
262 Irp->IoStatus.Status = STATUS_SUCCESS;
263
264 Irp->IoStatus.Information = ReadSize;
265 Stack->Parameters.Read.Length = ReadSize;
266
267 IoStartNextPacket(DeviceObject, FALSE);
268 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
269 } else {
270 DeviceExtension->ReadIsPending = TRUE;
271 }
272 }
273
274 NTSTATUS STDCALL_FUNC MouseClassInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
275 {
276 // Retrieve GDI's callback
277
278 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
279 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
280 NTSTATUS status;
281
282 switch(Stack->Parameters.DeviceIoControl.IoControlCode)
283 {
284 case IOCTL_INTERNAL_MOUSE_CONNECT:
285
286 DeviceExtension->GDIInformation =
287 *((PGDI_INFORMATION)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
288
289 DPRINT("MouseClassInternalDeviceControl() installed GDI callback at %p\n", DeviceExtension->GDIInformation.CallBack);
290
291 status = STATUS_SUCCESS;
292 break;
293
294 case IOCTL_INTERNAL_MOUSE_DISCONNECT:
295
296 DeviceExtension->GDIInformation.CallBack = NULL;
297
298 status = STATUS_SUCCESS;
299 break;
300
301 default:
302 status = STATUS_INVALID_DEVICE_REQUEST;
303 break;
304 }
305
306 Irp->IoStatus.Status = status;
307 if (status == STATUS_PENDING) {
308 IoMarkIrpPending(Irp);
309 IoStartPacket(DeviceObject, Irp, NULL, NULL);
310 } else {
311 IoCompleteRequest(Irp, IO_NO_INCREMENT);
312 }
313
314 return status;
315 }
316
317 NTSTATUS STDCALL
318 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
319 {
320 PDEVICE_OBJECT DeviceObject;
321 UNICODE_STRING DeviceName = UNICODE_STRING_INITIALIZER(L"\\Device\\MouseClass");
322 UNICODE_STRING SymlinkName = UNICODE_STRING_INITIALIZER(L"\\??\\MouseClass"); NTSTATUS Status;
323
324
325 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)MouseClassDispatch;
326 // DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)MouseClassDispatch;
327 // DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)MouseClassDispatch;
328 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = (PDRIVER_DISPATCH)MouseClassInternalDeviceControl; // to get GDI callback
329 // DriverObject->DriverStartIo = (PDRIVER_STARTIO)MouseClassStartIo;
330
331 Status = IoCreateDevice(DriverObject,
332 sizeof(DEVICE_EXTENSION),
333 &DeviceName,
334 FILE_DEVICE_MOUSE,
335 0,
336 TRUE,
337 &DeviceObject);
338 if (!NT_SUCCESS(Status))
339 {
340 return(Status);
341 }
342 DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
343
344 Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
345 if (!NT_SUCCESS(Status))
346 {
347 IoDeleteDevice(DeviceObject);
348 return(Status);
349 }
350
351 Status = ConnectMousePortDriver(DeviceObject);
352 if (!NT_SUCCESS(Status))
353 {
354 IoDeleteSymbolicLink(&SymlinkName);
355 IoDeleteDevice(DeviceObject);
356 return(Status);
357 }
358 return(STATUS_SUCCESS);
359 }