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