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