update for HEAD-2003021201
[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 "../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 }