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