2 * Serial Mouse driver 0.0.8
3 * Written by Jason Filby (jasonfilby@yahoo.com)
4 * And heavily rewritten by Filip Navara (xnavara@volny.cz)
5 * For ReactOS (www.reactos.com)
7 * Technical information about mouse protocols can be found
8 * in the file sermouse.txt.
11 #include <ddk/ntddk.h>
12 #include <ddk/ntddmou.h>
15 * Compile time options
18 /* Support for the IOCTL_MOUSE_QUERY_ATTRIBUTES I/O control code */
19 #define SERMOUSE_QUERYATTRIBUTES_SUPPORT
20 /* Check for mouse on COM1? */
21 #define SERMOUSE_COM1_SUPPORT
22 /* Check for mouse on COM2? */
23 //#define SERMOUSE_COM2_SUPPORT
24 /* Create \??\Mouse* symlink for device? */
25 #define SERMOUSE_MOUSESYMLINK_SUPPORT
31 #define MOUSE_IRQ_COM1 4
32 #define MOUSE_IRQ_COM2 3
33 #define MOUSE_PORT_COM1 0x3f8
34 #define MOUSE_PORT_COM2 0x2f8
36 /* Maximum value plus one for \Device\PointerClass* device name */
37 #define POINTER_PORTS_MAXIMUM 8
38 /* Letter count for POINTER_PORTS_MAXIMUM variable * sizeof(WCHAR) */
39 #define SUFFIX_MAXIMUM_SIZE (1 * sizeof(WCHAR))
42 #define MOUSE_TYPE_NONE 0
43 /* Microsoft Mouse with 2 buttons */
44 #define MOUSE_TYPE_MICROSOFT 1
45 /* Logitech Mouse with 3 buttons */
46 #define MOUSE_TYPE_LOGITECH 2
47 /* Microsoft Wheel Mouse (aka Z Mouse) */
48 #define MOUSE_TYPE_WHEELZ 3
49 /* Mouse Systems Mouse */
50 #define MOUSE_TYPE_MOUSESYSTEMS 4
52 /* Size for packet buffer used in interrupt routine */
53 #define PACKET_BUFFER_SIZE 4
55 /* Hardware byte mask for left button */
56 #define LEFT_BUTTON_MASK 0x20
57 /* Hardware to Microsoft specific code byte shift for left button */
58 #define LEFT_BUTTON_SHIFT 5
59 /* Hardware byte mask for right button */
60 #define RIGHT_BUTTON_MASK 0x10
61 /* Hardware to Microsoft specific code byte shift for right button */
62 #define RIGHT_BUTTON_SHIFT 3
63 /* Hardware byte mask for middle button */
64 #define MIDDLE_BUTTON_MASK 0x20
65 /* Hardware to Microsoft specific code byte shift for middle button */
66 #define MIDDLE_BUTTON_SHIFT 3
68 /* Microsoft byte mask for left button */
69 #define MOUSE_BUTTON_LEFT 0x01
70 /* Microsoft byte mask for right button */
71 #define MOUSE_BUTTON_RIGHT 0x02
72 /* Microsoft byte mask for middle button */
73 #define MOUSE_BUTTON_MIDDLE 0x04
79 typedef struct _DEVICE_EXTENSION {
80 PDEVICE_OBJECT DeviceObject;
82 ULONG InputDataCount[2];
83 MOUSE_INPUT_DATA MouseInputData[2][MOUSE_BUFFER_SIZE];
84 CLASS_INFORMATION ClassInformation;
85 PKINTERRUPT MouseInterrupt;
89 UCHAR PacketBuffer[PACKET_BUFFER_SIZE];
90 ULONG PacketBufferPosition;
91 ULONG PreviousButtons;
92 #ifdef SERMOUSE_QUERYATTRIBUTES_SUPPORT
93 MOUSE_ATTRIBUTES AttributesInformation;
95 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
101 void ClearMouse(ULONG Port)
103 /* Waits until the mouse calms down but also quits out after a while
104 * in case some destructive user wants to keep moving the mouse
105 * before we're done */
106 unsigned int Restarts = 0, i;
107 for (i = 0; i < 60000; i++)
109 unsigned Temp = READ_PORT_UCHAR((PUCHAR)Port);
113 if (Restarts < 300000)
122 SerialMouseInterruptService(IN PKINTERRUPT Interrupt, PVOID ServiceContext)
124 PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
125 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
126 UCHAR *PacketBuffer = DeviceExtension->PacketBuffer;
127 ULONG MousePort = DeviceExtension->MousePort;
128 UCHAR InterruptId = READ_PORT_UCHAR((PUCHAR)MousePort + 2);
131 PMOUSE_INPUT_DATA Input;
132 ULONG ButtonsDifference;
134 /* Is the interrupt for us? */
135 if ((InterruptId & 0x01) == 0x01)
140 /* Not a Receive Data Available interrupt? */
141 if ((InterruptId & 0x04) == 0)
146 /* Read all available data and process */
147 while ((READ_PORT_UCHAR((PUCHAR)MousePort + 5) & 0x01) != 0)
149 RecievedByte = READ_PORT_UCHAR((PUCHAR)MousePort);
152 if ((RecievedByte & 0x40) == 0x40)
153 DeviceExtension->PacketBufferPosition = 0;
155 PacketBuffer[DeviceExtension->PacketBufferPosition] =
156 (RecievedByte & 0x7f);
157 ++DeviceExtension->PacketBufferPosition;
159 /* Process packet if complete */
160 if (DeviceExtension->PacketBufferPosition >= 3)
162 Queue = DeviceExtension->ActiveQueue % 2;
164 /* Prevent buffer overflow */
165 if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
168 Input = &DeviceExtension->MouseInputData[Queue][DeviceExtension->InputDataCount[Queue]];
170 if (DeviceExtension->PacketBufferPosition == 3)
172 /* Retrieve change in x and y from packet */
173 Input->LastX = (signed char)(PacketBuffer[1] | ((PacketBuffer[0] & 0x03) << 6));
174 Input->LastY = (signed char)(PacketBuffer[2] | ((PacketBuffer[0] & 0x0c) << 4));
176 /* Determine the current state of the buttons */
177 Input->RawButtons = (DeviceExtension->PreviousButtons & MOUSE_BUTTON_MIDDLE) |
178 ((UCHAR)(PacketBuffer[0] & LEFT_BUTTON_MASK) >> LEFT_BUTTON_SHIFT) |
179 ((UCHAR)(PacketBuffer[0] & RIGHT_BUTTON_MASK) >> RIGHT_BUTTON_SHIFT);
181 if (DeviceExtension->PacketBufferPosition == 4)
183 DeviceExtension->PacketBufferPosition = 0;
184 /* If middle button state changed than report event */
185 if (((UCHAR)(PacketBuffer[3] & MIDDLE_BUTTON_MASK) >> MIDDLE_BUTTON_SHIFT) ^
186 (DeviceExtension->PreviousButtons & MOUSE_BUTTON_MIDDLE))
188 Input->RawButtons ^= MOUSE_BUTTON_MIDDLE;
198 /* Determine ButtonFlags */
199 Input->ButtonFlags = 0;
200 ButtonsDifference = DeviceExtension->PreviousButtons ^ Input->RawButtons;
202 if (ButtonsDifference != 0)
204 if (ButtonsDifference & MOUSE_BUTTON_LEFT)
206 if (Input->RawButtons & MOUSE_BUTTON_LEFT)
207 Input->ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
209 Input->ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
211 if (ButtonsDifference & MOUSE_BUTTON_RIGHT)
213 if (Input->RawButtons & MOUSE_BUTTON_RIGHT)
214 Input->ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
216 Input->ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
218 if (ButtonsDifference & MOUSE_BUTTON_MIDDLE)
220 if (Input->RawButtons & MOUSE_BUTTON_MIDDLE)
221 Input->ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
223 Input->ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
227 /* Send the Input data to the Mouse Class driver */
228 DeviceExtension->InputDataCount[Queue]++;
229 KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp, NULL);
231 /* Copy RawButtons to Previous Buttons for Input */
232 DeviceExtension->PreviousButtons = Input->RawButtons;
240 SerialMouseInitializeDataQueue(PVOID Context)
245 MouseSynchronizeRoutine(PVOID Context)
251 SerialMouseStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
253 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
255 if (KeSynchronizeExecution(DeviceExtension->MouseInterrupt, MouseSynchronizeRoutine, Irp))
257 Irp->IoStatus.Status = STATUS_SUCCESS;
258 Irp->IoStatus.Information = 0;
259 IoCompleteRequest(Irp, IO_NO_INCREMENT);
260 IoStartNextPacket(DeviceObject, FALSE);
265 SerialMouseInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
267 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
268 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
271 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
273 case IOCTL_INTERNAL_MOUSE_CONNECT:
274 DeviceExtension->ClassInformation =
275 *((PCLASS_INFORMATION)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
277 /* Reinitialize the port input data queue synchronously */
278 KeSynchronizeExecution(DeviceExtension->MouseInterrupt,
279 (PKSYNCHRONIZE_ROUTINE)SerialMouseInitializeDataQueue,
282 Status = STATUS_SUCCESS;
285 #ifdef SERMOUSE_QUERYATTRIBUTES_SUPPORT
286 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
287 if (Stack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(MOUSE_ATTRIBUTES))
289 *(PMOUSE_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer =
290 DeviceExtension->AttributesInformation;
291 Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
292 Status = STATUS_SUCCESS;
294 Status = STATUS_BUFFER_TOO_SMALL;
300 Status = STATUS_INVALID_DEVICE_REQUEST;
304 Irp->IoStatus.Status = Status;
305 if (Status == STATUS_PENDING)
307 IoMarkIrpPending(Irp);
308 IoStartPacket(DeviceObject, Irp, NULL, NULL);
312 IoCompleteRequest(Irp, IO_NO_INCREMENT);
319 SerialMouseDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
321 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
324 switch (Stack->MajorFunction)
328 Status = STATUS_SUCCESS;
332 DbgPrint("NOT IMPLEMENTED\n");
333 Status = STATUS_NOT_IMPLEMENTED;
337 if (Status == STATUS_PENDING)
339 IoMarkIrpPending(Irp);
343 Irp->IoStatus.Status = Status;
344 Irp->IoStatus.Information = 0;
345 IoCompleteRequest(Irp, IO_NO_INCREMENT);
351 VOID SerialMouseIsrDpc(PKDPC Dpc, PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
353 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
356 Queue = DeviceExtension->ActiveQueue % 2;
357 InterlockedIncrement(&DeviceExtension->ActiveQueue);
358 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassInformation.CallBack)(
359 DeviceExtension->ClassInformation.DeviceObject,
360 DeviceExtension->MouseInputData[Queue],
362 &DeviceExtension->InputDataCount[Queue]);
364 DeviceExtension->InputDataCount[Queue] = 0;
367 void InitializeSerialPort(ULONG Port, unsigned int LineControl)
369 WRITE_PORT_UCHAR((PUCHAR)Port + 3, 0x80); /* set DLAB on */
370 WRITE_PORT_UCHAR((PUCHAR)Port, 0x60); /* speed LO byte */
371 WRITE_PORT_UCHAR((PUCHAR)Port + 1, 0); /* speed HI byte */
372 WRITE_PORT_UCHAR((PUCHAR)Port + 3, LineControl);
373 WRITE_PORT_UCHAR((PUCHAR)Port + 1, 0); /* set comm and DLAB to 0 */
374 WRITE_PORT_UCHAR((PUCHAR)Port + 4, 0x09); /* DR int enable */
375 (void) READ_PORT_UCHAR((PUCHAR)Port + 5); /* clear error bits */
378 ULONG DetectMicrosoftMouse(ULONG Port)
385 /* Shutdown mouse or something like that */
386 LineControl = READ_PORT_UCHAR((PUCHAR)Port + 4);
387 WRITE_PORT_UCHAR((PUCHAR)Port + 4, (LineControl & ~0x02) | 0x01);
388 KeStallExecutionProcessor(500000);
391 while (READ_PORT_UCHAR((PUCHAR)Port + 5) & 0x01)
392 (void)READ_PORT_UCHAR((PUCHAR)Port);
394 /* Send modem control with 'Data Terminal Ready', 'Request To Send' and
395 * 'Output Line 2' message. This enables mouse to identify. */
396 WRITE_PORT_UCHAR((PUCHAR)Port + 4, 0x0b);
397 /* Wait 10 milliseconds for the mouse getting ready */
398 KeStallExecutionProcessor(10000);
400 /* Read first four bytes, which contains Microsoft Mouse signs */
401 for (i = 0; i < 4; i++)
403 while (((READ_PORT_UCHAR((PUCHAR)Port + 5) & 1) == 0) && (TimeOut > 0))
405 KeStallExecutionProcessor(1000);
408 return MOUSE_TYPE_NONE;
410 Buffer[i] = READ_PORT_UCHAR((PUCHAR)Port);
413 /* Check that four bytes for signs */
414 for (i = 0; i < 4; ++i)
416 /* Sign for Microsoft Ballpoint */
417 if (Buffer[i] == 'B')
419 DbgPrint("Microsoft Ballpoint device detected");
420 DbgPrint("THIS DEVICE IS NOT SUPPORTED, YET");
421 return MOUSE_TYPE_NONE;
423 /* Sign for Microsoft Mouse protocol followed by button specifier */
424 if (Buffer[i] == 'M')
429 return MOUSE_TYPE_NONE;
431 switch (Buffer[i + 1])
434 DbgPrint("Microsoft Mouse with 3-buttons detected\n");
435 return MOUSE_TYPE_LOGITECH;
437 DbgPrint("Microsoft Wheel Mouse detected\n");
438 return MOUSE_TYPE_WHEELZ;
441 DbgPrint("Microsoft Mouse with 2-buttons detected\n");
442 return MOUSE_TYPE_MICROSOFT;
447 return MOUSE_TYPE_NONE;
451 AllocatePointerDevice(PDRIVER_OBJECT DriverObject)
453 PDEVICE_OBJECT DeviceObject;
454 UNICODE_STRING DeviceName;
455 UNICODE_STRING SuffixString;
456 UNICODE_STRING SymlinkName;
457 PDEVICE_EXTENSION DeviceExtension;
461 /* Allocate buffer for full device name */
462 RtlInitUnicodeString(&DeviceName, NULL);
463 DeviceName.MaximumLength = sizeof(DD_MOUSE_DEVICE_NAME_U) + SUFFIX_MAXIMUM_SIZE + sizeof(UNICODE_NULL);
464 DeviceName.Buffer = ExAllocatePool(PagedPool, DeviceName.MaximumLength);
465 RtlAppendUnicodeToString(&DeviceName, DD_MOUSE_DEVICE_NAME_U);
467 /* Allocate buffer for device name suffix */
468 RtlInitUnicodeString(&SuffixString, NULL);
469 SuffixString.MaximumLength = SUFFIX_MAXIMUM_SIZE + sizeof(UNICODE_NULL);
470 SuffixString.Buffer = ExAllocatePool(PagedPool, SuffixString.MaximumLength);
472 /* Generate full qualified name with suffix */
473 for (Suffix = 0; Suffix < POINTER_PORTS_MAXIMUM; ++Suffix)
475 RtlIntegerToUnicodeString(Suffix, 10, &SuffixString);
476 RtlAppendUnicodeToString(&DeviceName, SuffixString.Buffer);
477 Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
478 &DeviceName, FILE_DEVICE_SERIAL_MOUSE_PORT, 0, TRUE, &DeviceObject);
479 /* Device successfully created, leave the cyclus */
480 if (NT_SUCCESS(Status))
482 DeviceName.Length -= SuffixString.Length;
485 ExFreePool(DeviceName.Buffer);
487 /* Couldn't create device */
488 if (!NT_SUCCESS(Status))
490 ExFreePool(SuffixString.Buffer);
494 DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
496 #ifdef SERMOUSE_MOUSESYMLINK_SUPPORT
498 /* FIXME: Why? FiN 20/08/2003 */
499 RtlInitUnicodeString(&SymlinkName, NULL);
500 SymlinkName.MaximumLength = sizeof(L"\\??\\Mouse") + SUFFIX_MAXIMUM_SIZE + sizeof(UNICODE_NULL);
501 SymlinkName.Buffer = ExAllocatePool(PagedPool, SymlinkName.MaximumLength);
502 RtlAppendUnicodeToString(&SymlinkName, L"\\??\\Mouse");
503 RtlAppendUnicodeToString(&DeviceName, SuffixString.Buffer);
504 IoCreateSymbolicLink(&SymlinkName, &DeviceName);
506 ExFreePool(SuffixString.Buffer);
508 DeviceExtension = DeviceObject->DeviceExtension;
509 KeInitializeDpc(&DeviceExtension->IsrDpc, (PKDEFERRED_ROUTINE)SerialMouseIsrDpc, DeviceObject);
515 InitializeMouse(ULONG Port, ULONG Irq, PDRIVER_OBJECT DriverObject)
517 PDEVICE_EXTENSION DeviceExtension;
518 PDEVICE_OBJECT DeviceObject;
524 /* Try to detect mouse on specified port */
525 InitializeSerialPort(Port, 2);
526 MouseType = DetectMicrosoftMouse(Port);
528 /* Enable interrupts */
529 WRITE_PORT_UCHAR((PUCHAR)(Port) + 1, 1);
532 /* No mouse, no need to continue */
533 if (MouseType == MOUSE_TYPE_NONE)
536 /* Allocate new device */
537 DeviceObject = AllocatePointerDevice(DriverObject);
540 DbgPrint("Oops, couldn't creat device object.\n");
543 DeviceExtension = DeviceObject->DeviceExtension;
545 /* Setup device extension structure */
546 DeviceExtension->ActiveQueue = 0;
547 DeviceExtension->MouseType = MouseType;
548 DeviceExtension->MousePort = Port;
549 DeviceExtension->PacketBufferPosition = 0;
550 DeviceExtension->PreviousButtons = 0;
551 #ifdef SERMOUSE_QUERYATTRIBUTES_SUPPORT
554 case MOUSE_TYPE_MICROSOFT:
555 DeviceExtension->AttributesInformation.MouseIdentifier = MOUSE_SERIAL_HARDWARE;
556 DeviceExtension->AttributesInformation.NumberOfButtons = 2;
558 case MOUSE_TYPE_LOGITECH:
559 DeviceExtension->AttributesInformation.MouseIdentifier = MOUSE_SERIAL_HARDWARE;
560 DeviceExtension->AttributesInformation.NumberOfButtons = 3;
562 case MOUSE_TYPE_WHEELZ:
563 DeviceExtension->AttributesInformation.MouseIdentifier = WHEELMOUSE_SERIAL_HARDWARE;
564 DeviceExtension->AttributesInformation.NumberOfButtons = 3;
567 DeviceExtension->AttributesInformation.SampleRate = 40;
568 DeviceExtension->AttributesInformation.InputDataQueueLength = MOUSE_BUFFER_SIZE;
571 MappedIrq = HalGetInterruptVector(Internal, 0, 0, Irq, &Dirql, &Affinity);
574 &DeviceExtension->MouseInterrupt, SerialMouseInterruptService,
575 DeviceObject, NULL, MappedIrq, Dirql, Dirql, 0, FALSE,
582 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
584 BOOL MouseFound = FALSE;
586 DbgPrint("Serial Mouse Driver 0.0.8\n");
587 #ifdef SERMOUSE_COM1_SUPPORT
588 DbgPrint("Trying to find mouse on COM1\n");
589 MouseFound |= InitializeMouse(MOUSE_PORT_COM1, MOUSE_IRQ_COM1, DriverObject);
591 #ifdef SERMOUSE_COM2_SUPPORT
592 DbgPrint("Trying to find mouse on COM2\n");
593 MouseFound |= InitializeMouse(MOUSE_PORT_COM2, MOUSE_IRQ_COM2, DriverObject);
598 DbgPrint("No serial mouse found.\n");
599 return STATUS_UNSUCCESSFUL;
602 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)SerialMouseDispatch;
603 DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)SerialMouseDispatch;
604 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = (PDRIVER_DISPATCH)SerialMouseInternalDeviceControl;
605 DriverObject->DriverStartIo = SerialMouseStartIo;
607 return STATUS_SUCCESS;