update for HEAD-2003050101
[reactos.git] / 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\\Mouse");
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       DbgPrint("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 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         DbgPrint("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 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          DbgPrint("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");
322
323    DriverObject->MajorFunction[IRP_MJ_CREATE] = MouseClassDispatch;
324 //   DriverObject->MajorFunction[IRP_MJ_CLOSE]  = MouseClassDispatch;
325 //   DriverObject->MajorFunction[IRP_MJ_READ]   = MouseClassDispatch;
326    DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MouseClassInternalDeviceControl; // to get GDI callback
327 //   DriverObject->DriverStartIo                = MouseClassStartIo;
328
329    IoCreateDevice(DriverObject,
330                   sizeof(DEVICE_EXTENSION),
331                   &DeviceName,
332                   FILE_DEVICE_MOUSE,
333                   0,
334                   TRUE,
335                   &DeviceObject);
336    DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
337
338    IoCreateSymbolicLink(&SymlinkName, &DeviceName);
339
340    return ConnectMousePortDriver(DeviceObject);
341 }