update for HEAD-2003091401
[reactos.git] / drivers / input / sermouse / sermouse.c
index 2772b5d..c1f6356 100644 (file)
 /*
- * Mouse driver 0.0.6
+ * Serial Mouse driver 0.0.8
  * Written by Jason Filby (jasonfilby@yahoo.com)
+ * And heavily rewritten by Filip Navara (xnavara@volny.cz)
  * For ReactOS (www.reactos.com)
  *
- * Note: The serial.o driver must be loaded before loading this driver
- *
- * Known Limitations:
- * Only supports Microsoft mice on COM port 1
- *
- * Following information obtained from Tomi Engdahl (then@delta.hut.fi),
- * http://www.hut.fi/~then/mytexts/mouse.html
- *
- * Microsoft serial mouse
- *
- *   Serial data parameters:
- *     1200bps, 7 databits, 1 stop-bit
- *
- *   Data packet format:
- *     Data packet is 3 byte packet. It is send to the computer every time mouse
- *     state changes (mouse moves or keys are pressed/released). 
- *         D7      D6      D5      D4      D3      D2      D1      D0
- *     1.  X       1       LB      RB      Y7      Y6      X7      X6
- *     2.  X       0       X5      X4      X3      X2      X1      X0      
- *     3.  X       0       Y5      Y4      Y3      Y2      Y1      Y0
- *
- *     Note: The bit marked with X is 0 if the mouse received with 7 databits
- *     and 2 stop bits format. It is also possible to use 8 databits and 1 stop
- *     bit format for receiving. In this case X gets value 1. The safest thing
- *     to get everything working is to use 7 databits and 1 stopbit when
- *     receiving mouse information (and if you are making mouse then send out
- *     7 databits and 2 stop bits). 
- *     The byte marked with 1. is send first, then the others. The bit D6 in
- *     the first byte is used for syncronizing the software to mouse packets
- *     if it goes out of sync. 
- *
- *      LB is the state of the left button (1 means pressed down)
- *      RB is the state of the right button (1 means pressed down)
- *      X7-X0 movement in X direction since last packet (signed byte)
- *      Y7-Y0 movement in Y direction since last packet (signed byte)
- *
- *    Mouse identification
- *      When DTR line is toggled, mouse should send one data byte containing
- *      letter 'M' (ascii 77).
- *
- *
- * Logitech serial mouse
- *
- *   Logitech uses the Microsoft serial mouse protocol in their mouses (for
- *   example Logitech Pilot mouse and others). The origianal protocol supports
- *   only two buttons, but logitech as added third button to some of their
- *   mouse models. To make this possible logitech has made one extension to
- *   the protocol. 
- *   I have not seen any documentation about the exact documents, but here is
- *   what I have found out: The information of the third button state is sent
- *   using one extra byte which is send after the normal packet when needed.
- *   Value 32 (dec) is sent every time when the center button is pressed down.
- *   It is also sent every time with the data packet when center button is kept
- *   down and the mouse data packet is sent for other reasons. When center
- *   button is released, the mouse sends the normal data packet followed by
- *   data bythe which has value 0 (dec). As you can see the extra data byte
- *   is sent only when you mess with the center button.
- *
- *
- * Mouse systems mouse
- *
- *   Serial data parameters:
- *     1200bps, 8 databits, 1 stop-bit
- *
- *   Data packet format:
- *          D7      D6      D5      D4      D3      D2      D1      D0
- *     1.   1       0       0       0       0       LB      CB      RB
- *     2.   X7      X6      X5      X4      X3      X2      X1      X0
- *     3.   Y7      Y6      Y5      Y4      Y3      Y4      Y1      Y0
- *     4.   X7'     X6'     X5'     X4'     X3'     X2'     X1'     X0'
- *     5.   Y7'     Y6'     Y5'     Y4'     Y3'     Y4'     Y1'     Y0'
- *
- *     LB is left button state (0 = pressed, 1 = released)
- *     CB is center button state (0 = pressed, 1 = released)
- *     RB is right button state (0 = pressed, 1 = released)
- *     X7-X0 movement in X direction since last packet in signed byte 
- *           format (-128..+127), positive direction right
- *     Y7-Y0 movement in Y direction since last packet in signed byte 
- *           format (-128..+127), positive direction up
- *     X7'-X0' movement in X direction since sending of X7-X0 packet in
- *             signed byte format (-128..+127), positive direction right
- *     Y7'-Y0' movement in Y direction since sending of Y7-Y0 packet in
- *             signed byte format (-128..+127), positive direction up
- *
- *     The last two bytes in the packet (bytes 4 and 5) contains information
- *     about movement data changes which have occured after data bytes 2 and 3
- *     have been sent.
- *
+ * Technical information about mouse protocols can be found
+ * in the file sermouse.txt.
  */
 
 #include <ddk/ntddk.h>
 #include <ddk/ntddmou.h>
 
-#define MOUSE_IRQ_COM1  4
-#define MOUSE_IRQ_COM2  3
-
-#define MOUSE_PORT_COM1 0x3f8
-#define MOUSE_PORT_COM2 0x2f8
-
-typedef struct _DEVICE_EXTENSION {
-  PDEVICE_OBJECT DeviceObject;
-  ULONG ActiveQueue;
-  ULONG InputDataCount[2];
-  MOUSE_INPUT_DATA MouseInputData[2][MOUSE_BUFFER_SIZE];
-  CLASS_INFORMATION ClassInformation;
-
-  PKINTERRUPT MouseInterrupt;
-  KDPC IsrDpc;
-} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
-
-static unsigned int MOUSE_IRQ = MOUSE_IRQ_COM1;
-static unsigned int MOUSE_COM = MOUSE_PORT_COM1;
-
-static unsigned int     bytepos=0, coordinate;
-static unsigned char    mpacket[3];
-static unsigned char    mouse_button1, mouse_button2;
+/*
+ * Compile time options
+ */
 
-// Previous button state
-static ULONG PreviousButtons = 0;
+/* Support for the IOCTL_MOUSE_QUERY_ATTRIBUTES I/O control code */
+#define SERMOUSE_QUERYATTRIBUTES_SUPPORT
+/* Check for mouse on COM1? */
+#define SERMOUSE_COM1_SUPPORT
+/* Check for mouse on COM2? */
+//#define SERMOUSE_COM2_SUPPORT
+/* Create \??\Mouse* symlink for device? */
+#define SERMOUSE_MOUSESYMLINK_SUPPORT
 
-BOOLEAN STDCALL
-microsoft_mouse_handler(IN PKINTERRUPT Interrupt, PVOID ServiceContext)
-{
-  PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
-  PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
-  PMOUSE_INPUT_DATA Input;
-  ULONG Queue, ButtonsDiff;
-  unsigned int mbyte;
-  int change_x;
-  int change_y;
-  UCHAR InterruptId = READ_PORT_UCHAR((PUCHAR)MOUSE_COM + 2);
-
-  /* Is the interrupt for us? */
-  if (0 != (InterruptId & 0x01))
-  {
-    return FALSE;
-  }
-
-  /* Not a Receive Data Available interrupt? */
-  if (0 == (InterruptId & 0x04))
-  {
-    return TRUE;
-  }
-
-  /* Read all available data and process */
-  while (0 != (READ_PORT_UCHAR((PUCHAR)MOUSE_COM + 5) & 0x01))
-    {
-    mbyte = READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
+/*
+ * Definitions
+ */
 
-    /* Synchronize */
-    if (0x40 == (mbyte & 0x40))
-      bytepos=0;
+#define MOUSE_IRQ_COM1                 4
+#define MOUSE_IRQ_COM2                 3
+#define MOUSE_PORT_COM1                        0x3f8
+#define MOUSE_PORT_COM2                        0x2f8
+
+/* Maximum value plus one for \Device\PointerClass* device name */
+#define POINTER_PORTS_MAXIMUM  8
+/* Letter count for POINTER_PORTS_MAXIMUM variable * sizeof(WCHAR) */
+#define SUFFIX_MAXIMUM_SIZE            (1 * sizeof(WCHAR))
+
+/* No Mouse */
+#define MOUSE_TYPE_NONE                        0
+/* Microsoft Mouse with 2 buttons */
+#define MOUSE_TYPE_MICROSOFT   1
+/* Logitech Mouse with 3 buttons */
+#define MOUSE_TYPE_LOGITECH            2
+/* Microsoft Wheel Mouse (aka Z Mouse) */
+#define MOUSE_TYPE_WHEELZ              3
+/* Mouse Systems Mouse */
+#define MOUSE_TYPE_MOUSESYSTEMS        4
+
+/* Size for packet buffer used in interrupt routine */
+#define PACKET_BUFFER_SIZE             4
+
+/* Hardware byte mask for left button */
+#define LEFT_BUTTON_MASK               0x20
+/* Hardware to Microsoft specific code byte shift for left button */
+#define LEFT_BUTTON_SHIFT              5
+/* Hardware byte mask for right button */
+#define RIGHT_BUTTON_MASK              0x10
+/* Hardware to Microsoft specific code byte shift for right button */
+#define RIGHT_BUTTON_SHIFT             3
+/* Hardware byte mask for middle button */
+#define MIDDLE_BUTTON_MASK             0x20
+/* Hardware to Microsoft specific code byte shift for middle button */
+#define MIDDLE_BUTTON_SHIFT            3
+
+/* Microsoft byte mask for left button */
+#define MOUSE_BUTTON_LEFT              0x01
+/* Microsoft byte mask for right button */
+#define MOUSE_BUTTON_RIGHT             0x02
+/* Microsoft byte mask for middle button */
+#define MOUSE_BUTTON_MIDDLE            0x04
 
-    mpacket[bytepos] = (mbyte & 0x7f);
-    bytepos++;
+/*
+ * Structures
+ */
 
-    /* Process packet if complete */
-    if (3 == bytepos)
-    {
-      /* Set local variables for DeviceObject and DeviceExtension */
-      DeviceObject = (PDEVICE_OBJECT)ServiceContext;
-      DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
-      Queue = DeviceExtension->ActiveQueue % 2;
-
-      /* Prevent buffer overflow */
-      if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
-      {
-       continue;
-      }
-
-      Input = &DeviceExtension->MouseInputData[Queue]
-              [DeviceExtension->InputDataCount[Queue]];
-
-      /* Retrieve change in x and y from packet */
-      change_x = (int)(signed char)((mpacket[0] & 0x03) << 6) + mpacket[1];
-      change_y = (int)(signed char)((mpacket[0] & 0x0c) << 4) + mpacket[2];
-
-      /* Some mice need this */
-      if (1 == coordinate)
-      {
-        change_x-=128;
-        change_y-=128;
-      }
-
-#if 0
-      /* Change to signed */
-      if (128 <= change_x)
-      {
-       change_x = change_x - 256;
-      }
-      if (128 <= change_y)
-      {
-       change_y = change_y - 256;
-      }
+typedef struct _DEVICE_EXTENSION {
+       PDEVICE_OBJECT DeviceObject;
+       ULONG ActiveQueue;
+       ULONG InputDataCount[2];
+       MOUSE_INPUT_DATA MouseInputData[2][MOUSE_BUFFER_SIZE];
+       CLASS_INFORMATION ClassInformation;
+       PKINTERRUPT MouseInterrupt;
+       KDPC IsrDpc;
+       ULONG MousePort;
+       ULONG MouseType;
+       UCHAR PacketBuffer[PACKET_BUFFER_SIZE];
+       ULONG PacketBufferPosition;
+       ULONG PreviousButtons;
+#ifdef SERMOUSE_QUERYATTRIBUTES_SUPPORT
+       MOUSE_ATTRIBUTES AttributesInformation;
 #endif
+} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
 
-      Input->LastX = 2 * change_x;
-      Input->LastY = - 3 * change_y;
-
-      /* Retrieve mouse button status from packet */
-      mouse_button1 = mpacket[0] & 0x20;
-      mouse_button2 = mpacket[0] & 0x10;
-    
-      /* Determine the current state of the buttons */
-      Input->RawButtons = mouse_button1 + mouse_button2;
-    
-      /* Determine ButtonFlags */
-      Input->ButtonFlags = 0;
-      ButtonsDiff = PreviousButtons ^ Input->RawButtons;
+/*
+ * Functions
+ */
 
-      if (0 != (ButtonsDiff & 0x20))
-      {
-       if (0 != (Input->RawButtons & 0x20))
-       {
-         Input->ButtonFlags |= MOUSE_BUTTON_1_DOWN;
-       }
-       else
+void ClearMouse(ULONG Port)
+{
+       /* Waits until the mouse calms down but also quits out after a while
+        * in case some destructive user wants to keep moving the mouse
+        * before we're done */
+       unsigned int Restarts = 0, i;
+       for (i = 0; i < 60000; i++)
        {
-         Input->ButtonFlags |= MOUSE_BUTTON_1_UP;
+       unsigned Temp = READ_PORT_UCHAR((PUCHAR)Port);
+           if (Temp != 0)
+       {
+                       Restarts++;
+                       if (Restarts < 300000)
+                               i = 0;
+                       else
+                               i = 60000;
+               }
        }
-      }
+}
 
-      if (0 != (ButtonsDiff & 0x10))
-      {
-       if (0 != (Input->RawButtons & 0x10))
+BOOLEAN STDCALL
+SerialMouseInterruptService(IN PKINTERRUPT Interrupt, PVOID ServiceContext)
+{
+       PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
+       PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
+       UCHAR *PacketBuffer = DeviceExtension->PacketBuffer;
+       ULONG MousePort = DeviceExtension->MousePort;
+       UCHAR InterruptId = READ_PORT_UCHAR((PUCHAR)MousePort + 2);
+       UCHAR RecievedByte;
+       ULONG Queue;
+       PMOUSE_INPUT_DATA Input;
+       ULONG ButtonsDifference;
+
+       /* Is the interrupt for us? */
+       if ((InterruptId & 0x01) == 0x01)
        {
-         Input->ButtonFlags |= MOUSE_BUTTON_2_DOWN;
+               return FALSE;
        }
-        else
+
+       /* Not a Receive Data Available interrupt? */
+       if ((InterruptId & 0x04) == 0)
        {
-         Input->ButtonFlags |= MOUSE_BUTTON_2_UP;
+               return TRUE;
        }
-      }
 
-      bytepos=0;
-    
-      /* Send the Input data to the Mouse Class driver */
-      DeviceExtension->InputDataCount[Queue]++;
-      KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp, NULL);
-
-      /* Copy RawButtons to Previous Buttons for Input */
-      PreviousButtons = Input->RawButtons;
-    }
-  }
-
-  return TRUE;
-}
-
-void InitializeMouseHardware(unsigned int mtype)
-{
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 3, 0x80);  /* set DLAB on   */
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM,     0x60);  /* speed LO byte */
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 1, 0);     /* speed HI byte */
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 3, mtype); /* 2=MS Mouse; 3=Mouse systems mouse */
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 1, 0);     /* set comm and DLAB to 0 */
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 4, 0x09);  /* DR int enable */
-
-  (void) READ_PORT_UCHAR((PUCHAR)MOUSE_COM+5);    /* clear error bits */
-}
-
-int DetMicrosoft(void)
-{
-  char tmp, ind;
-  int buttons=0, i, timeout=250;
-  LARGE_INTEGER Timeout;
-
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM+4, 0x0b);
-  tmp=READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
-
-  /* Check the first four bytes for signs that this is an MS mouse */
-  for(i=0; i<4; i++) {
-    while(((READ_PORT_UCHAR((PUCHAR)MOUSE_COM+5) & 1)==0) && (timeout>0))
-    {
-      Timeout.QuadPart = 1;
-      KeDelayExecutionThread (KernelMode, FALSE, &Timeout);
-      timeout--;
-    }
-    ind=READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
-    if(ind==0x33) buttons=3;
-    if(ind==0x4d) buttons=2;
-  }
-
-  return buttons;
-}
-
-int CheckMouseType(unsigned int mtype)
-{
-  unsigned int retval=0;
-
-  InitializeMouseHardware(mtype);
-  if(mtype==2) retval=DetMicrosoft();
-  if(mtype==3) {
-    WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM+4, 11);
-    retval=3;
-  }
-  WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM+1, 1);
-
-  return retval;
-}
+       /* Read all available data and process */
+       while ((READ_PORT_UCHAR((PUCHAR)MousePort + 5) & 0x01) != 0)
+       {
+               RecievedByte = READ_PORT_UCHAR((PUCHAR)MousePort);
 
-void ClearMouse(void)
-{
-  /* Waits until the mouse calms down but also quits out after a while
-   * in case some destructive user wants to keep moving the mouse
-   * before we're done */
-
-  unsigned int restarts=0, i;
-  for (i=0; i<60000; i++)
-  {
-    unsigned temp=READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
-    if(temp!=0) {
-      restarts++;
-      if(restarts<300000) {
-        i=0;
-      } else
-      {
-        i=60000;
-      }
-    }
-  }
-}
+               /* Synchronize */
+               if ((RecievedByte & 0x40) == 0x40)
+                       DeviceExtension->PacketBufferPosition = 0;
 
-BOOLEAN InitializeMouse(PDEVICE_OBJECT DeviceObject)
-{
-  int mbuttons=0, gotmouse=0;
-  PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
-  ULONG MappedIrq;
-  KIRQL Dirql;
-  KAFFINITY Affinity;
-
-  /* Check for Microsoft mouse (2 buttons) */
-  if(CheckMouseType(2)!=0)
-  {
-    gotmouse=1;
-    DbgPrint("Microsoft Mouse Detected\n");
-    ClearMouse();
-    coordinate=0;
-  }
-
-  /* Check for Microsoft Systems mouse (3 buttons) */
-  if(gotmouse==0) {
-    if(CheckMouseType(3)!=0)
-    {
-    gotmouse=1;
-    DbgPrint("Mouse Systems Mouse Detected\n");
-    ClearMouse();
-    coordinate=1;
-    }
-  }
+               PacketBuffer[DeviceExtension->PacketBufferPosition] =
+                       (RecievedByte & 0x7f);
+               ++DeviceExtension->PacketBufferPosition;
 
-  if(gotmouse==0) return FALSE;
+               /* Process packet if complete */
+               if (DeviceExtension->PacketBufferPosition >= 3)
+               {
+                       Queue = DeviceExtension->ActiveQueue % 2;
+       
+                       /* Prevent buffer overflow */
+                       if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
+                               continue;
 
-  DeviceExtension->ActiveQueue    = 0;
-  MappedIrq = HalGetInterruptVector(Internal, 0, 0, MOUSE_IRQ, &Dirql, &Affinity);
+                       Input = &DeviceExtension->MouseInputData[Queue][DeviceExtension->InputDataCount[Queue]];
 
-  IoConnectInterrupt(&DeviceExtension->MouseInterrupt, microsoft_mouse_handler,
-                     DeviceObject, NULL, MappedIrq, Dirql, Dirql, 0, FALSE,
-                     Affinity, FALSE);
+                       if (DeviceExtension->PacketBufferPosition == 3)
+                       {
+                               /* Retrieve change in x and y from packet */
+               Input->LastX = (signed char)(PacketBuffer[1] | ((PacketBuffer[0] & 0x03) << 6));
+                   Input->LastY = (signed char)(PacketBuffer[2] | ((PacketBuffer[0] & 0x0c) << 4));
+       
+                               /* Determine the current state of the buttons */
+                               Input->RawButtons = (DeviceExtension->PreviousButtons & MOUSE_BUTTON_MIDDLE) |
+                                       ((UCHAR)(PacketBuffer[0] & LEFT_BUTTON_MASK) >> LEFT_BUTTON_SHIFT) |
+                                       ((UCHAR)(PacketBuffer[0] & RIGHT_BUTTON_MASK) >> RIGHT_BUTTON_SHIFT);
+                       } else
+                       if (DeviceExtension->PacketBufferPosition == 4)
+                       {
+                               DeviceExtension->PacketBufferPosition = 0;
+                               /* If middle button state changed than report event */
+                               if (((UCHAR)(PacketBuffer[3] & MIDDLE_BUTTON_MASK) >> MIDDLE_BUTTON_SHIFT) ^
+                                       (DeviceExtension->PreviousButtons & MOUSE_BUTTON_MIDDLE))
+                               {
+                                       Input->RawButtons ^= MOUSE_BUTTON_MIDDLE;
+                                       Input->LastX = 0;
+                                       Input->LastY = 0;
+                               }
+                               else
+                               {
+                                       continue;
+                               }
+                       }
+
+                       /* Determine ButtonFlags */
+                       Input->ButtonFlags = 0;
+                       ButtonsDifference = DeviceExtension->PreviousButtons ^ Input->RawButtons;
+
+                       if (ButtonsDifference != 0)
+                       {
+                               if (ButtonsDifference & MOUSE_BUTTON_LEFT)
+                               {
+                                       if (Input->RawButtons & MOUSE_BUTTON_LEFT)
+                                               Input->ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
+                                       else
+                                               Input->ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
+                               }
+                               if (ButtonsDifference & MOUSE_BUTTON_RIGHT)
+                               {
+                                       if (Input->RawButtons & MOUSE_BUTTON_RIGHT)
+                                               Input->ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
+                                       else
+                                               Input->ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
+                               }
+                               if (ButtonsDifference & MOUSE_BUTTON_MIDDLE)
+                               {
+                                       if (Input->RawButtons & MOUSE_BUTTON_MIDDLE)
+                                               Input->ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
+                                       else
+                                               Input->ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
+                               }
+                       }
+
+                       /* Send the Input data to the Mouse Class driver */
+                       DeviceExtension->InputDataCount[Queue]++;
+                       KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp, NULL);
+
+                       /* Copy RawButtons to Previous Buttons for Input */
+                       DeviceExtension->PreviousButtons = Input->RawButtons;
+               }
+       }
 
-  return TRUE;
+       return TRUE;
 }
 
-VOID SerialMouseInitializeDataQueue(PVOID Context)
+VOID
+SerialMouseInitializeDataQueue(PVOID Context)
 {
 }
 
 BOOLEAN STDCALL
 MouseSynchronizeRoutine(PVOID Context)
 {
-   PIRP Irp = (PIRP)Context;
-   PMOUSE_INPUT_DATA rec  = (PMOUSE_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
-   PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
-   ULONG NrToRead         = stk->Parameters.Read.Length/sizeof(MOUSE_INPUT_DATA);
-   int i;
-
-   if ((stk->Parameters.Read.Length/sizeof(MOUSE_INPUT_DATA))==NrToRead)
-   {
-      return(TRUE);
-   }
-
-   return(FALSE);
+       return TRUE;
 }
 
 VOID STDCALL
 SerialMouseStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
 {
-   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
+       PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
 
-   if (KeSynchronizeExecution(DeviceExtension->MouseInterrupt, MouseSynchronizeRoutine, Irp))
-     {
-       Irp->IoStatus.Status = STATUS_SUCCESS;
-       Irp->IoStatus.Information = 0;
-       IoCompleteRequest(Irp, IO_NO_INCREMENT);
-       IoStartNextPacket(DeviceObject, FALSE);
-     }
+       if (KeSynchronizeExecution(DeviceExtension->MouseInterrupt, MouseSynchronizeRoutine, Irp))
+       {
+               Irp->IoStatus.Status = STATUS_SUCCESS;
+               Irp->IoStatus.Information = 0;
+               IoCompleteRequest(Irp, IO_NO_INCREMENT);
+               IoStartNextPacket(DeviceObject, FALSE);
+       }
 }
 
 NTSTATUS STDCALL
 SerialMouseInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 {
-   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
-   PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
-   NTSTATUS status;
+       PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
+       PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+       NTSTATUS Status;
 
-   switch(Stack->Parameters.DeviceIoControl.IoControlCode)
-   {
-      case IOCTL_INTERNAL_MOUSE_CONNECT:
-
-         DeviceExtension->ClassInformation =
-            *((PCLASS_INFORMATION)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
-
-         /* Reinitialize the port input data queue synchronously */
-         KeSynchronizeExecution(DeviceExtension->MouseInterrupt,
-            (PKSYNCHRONIZE_ROUTINE)SerialMouseInitializeDataQueue, DeviceExtension);
-
-         status = STATUS_SUCCESS;
-         break;
+       switch (Stack->Parameters.DeviceIoControl.IoControlCode)
+       {
+               case IOCTL_INTERNAL_MOUSE_CONNECT:
+                       DeviceExtension->ClassInformation =
+                               *((PCLASS_INFORMATION)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
+
+                       /* Reinitialize the port input data queue synchronously */
+                       KeSynchronizeExecution(DeviceExtension->MouseInterrupt,
+                               (PKSYNCHRONIZE_ROUTINE)SerialMouseInitializeDataQueue,
+                               DeviceExtension);
+
+                       Status = STATUS_SUCCESS;
+                       break;
+
+#ifdef SERMOUSE_QUERYATTRIBUTES_SUPPORT
+               case IOCTL_MOUSE_QUERY_ATTRIBUTES:
+                       if (Stack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(MOUSE_ATTRIBUTES))
+                       {
+                               *(PMOUSE_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer =
+                                       DeviceExtension->AttributesInformation;
+                               Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
+                Status = STATUS_SUCCESS;                               
+            } else {
+                               Status = STATUS_BUFFER_TOO_SMALL;
+                       }
+                       break;
+#endif
 
-      default:
-         status = STATUS_INVALID_DEVICE_REQUEST;
-         break;
-   }
+               default:
+                       Status = STATUS_INVALID_DEVICE_REQUEST;
+                       break;
+       }
 
-   Irp->IoStatus.Status = status;
-   if (status == STATUS_PENDING) {
-      IoMarkIrpPending(Irp);
-      IoStartPacket(DeviceObject, Irp, NULL, NULL);
-   } else {
-      IoCompleteRequest(Irp, IO_NO_INCREMENT);
-   }
+       Irp->IoStatus.Status = Status;
+       if (Status == STATUS_PENDING)
+       {
+               IoMarkIrpPending(Irp);
+               IoStartPacket(DeviceObject, Irp, NULL, NULL);
+       }
+       else
+       {
+               IoCompleteRequest(Irp, IO_NO_INCREMENT);
+       }
 
-   return status;
+       return Status;
 }
 
 NTSTATUS STDCALL
 SerialMouseDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
 {
-   PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
-   NTSTATUS Status;
-   static BOOLEAN AlreadyOpened = FALSE;
-
-   switch (stk->MajorFunction)
-     {
-      case IRP_MJ_CREATE:
-       if (AlreadyOpened == TRUE)
-         {
-            Status = STATUS_SUCCESS;
-         }
+       PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+       NTSTATUS Status;
+
+       switch (Stack->MajorFunction)
+       {
+               case IRP_MJ_CREATE:
+               case IRP_MJ_CLOSE:
+                       Status = STATUS_SUCCESS;
+                       break;
+
+               default:
+                       DbgPrint("NOT IMPLEMENTED\n");
+                       Status = STATUS_NOT_IMPLEMENTED;
+                       break;
+       }
+
+       if (Status == STATUS_PENDING)
+       {
+               IoMarkIrpPending(Irp);
+       }
        else
-         {
-            Status = STATUS_SUCCESS;
-            AlreadyOpened = TRUE;
-         }
-       break;
-       
-      case IRP_MJ_CLOSE:
-        Status = STATUS_SUCCESS;
-       break;
-
-      default:
-        DbgPrint("NOT IMPLEMENTED\n");
-        Status = STATUS_NOT_IMPLEMENTED;
-       break;
-     }
-
-   if (Status==STATUS_PENDING)
-     {
-       IoMarkIrpPending(Irp);
-     }
-   else
-     {
-        Irp->IoStatus.Status = Status;
-       Irp->IoStatus.Information = 0;
-       IoCompleteRequest(Irp,IO_NO_INCREMENT);
-     }
-   return(Status);
+       {
+               Irp->IoStatus.Status = Status;
+               Irp->IoStatus.Information = 0;
+               IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+       return Status;
 }
 
 VOID SerialMouseIsrDpc(PKDPC Dpc, PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
@@ -497,45 +364,245 @@ VOID SerialMouseIsrDpc(PKDPC Dpc, PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID C
    DeviceExtension->InputDataCount[Queue] = 0;
 }
 
+void InitializeSerialPort(ULONG Port, unsigned int LineControl)
+{
+       WRITE_PORT_UCHAR((PUCHAR)Port + 3, 0x80);  /* set DLAB on   */
+       WRITE_PORT_UCHAR((PUCHAR)Port,     0x60);  /* speed LO byte */
+       WRITE_PORT_UCHAR((PUCHAR)Port + 1, 0);     /* speed HI byte */
+       WRITE_PORT_UCHAR((PUCHAR)Port + 3, LineControl);
+       WRITE_PORT_UCHAR((PUCHAR)Port + 1, 0);     /* set comm and DLAB to 0 */
+       WRITE_PORT_UCHAR((PUCHAR)Port + 4, 0x09);  /* DR int enable */
+       (void) READ_PORT_UCHAR((PUCHAR)Port + 5);  /* clear error bits */
+}
+
+ULONG DetectMicrosoftMouse(ULONG Port)
+{
+       CHAR Buffer[4];
+       ULONG i;
+       ULONG TimeOut = 250;
+    UCHAR LineControl;
+    
+    /* Shutdown mouse or something like that */ 
+    LineControl = READ_PORT_UCHAR((PUCHAR)Port + 4);
+       WRITE_PORT_UCHAR((PUCHAR)Port + 4, (LineControl & ~0x02) | 0x01);
+    KeStallExecutionProcessor(500000);
+
+    /* Clear buffer */
+       while (READ_PORT_UCHAR((PUCHAR)Port + 5) & 0x01)
+               (void)READ_PORT_UCHAR((PUCHAR)Port);
+
+       /* Send modem control with 'Data Terminal Ready', 'Request To Send' and
+        * 'Output Line 2' message. This enables mouse to identify. */
+       WRITE_PORT_UCHAR((PUCHAR)Port + 4, 0x0b);
+       /* Wait 10 milliseconds for the mouse getting ready */
+       KeStallExecutionProcessor(10000);
+
+       /* Read first four bytes, which contains Microsoft Mouse signs */
+       for (i = 0; i < 4; i++)
+       {
+               while (((READ_PORT_UCHAR((PUCHAR)Port + 5) & 1) == 0) && (TimeOut > 0))
+               {
+                       KeStallExecutionProcessor(1000);
+                       --TimeOut;
+                       if (TimeOut == 0)
+                               return MOUSE_TYPE_NONE;
+               }
+               Buffer[i] = READ_PORT_UCHAR((PUCHAR)Port);
+       }
+
+       /* Check that four bytes for signs */
+       for (i = 0; i < 4; ++i)
+       {
+               /* Sign for Microsoft Ballpoint */
+       if (Buffer[i] == 'B')
+           {
+                       DbgPrint("Microsoft Ballpoint device detected");
+                       DbgPrint("THIS DEVICE IS NOT SUPPORTED, YET");
+                       return MOUSE_TYPE_NONE;
+               } else
+               /* Sign for Microsoft Mouse protocol followed by button specifier */
+               if (Buffer[i] == 'M')
+               {
+                       if (i == 3)
+                       {
+                               /* Overflow Error */
+                               return MOUSE_TYPE_NONE;
+                       }
+                       switch (Buffer[i + 1])
+                       {
+                               case '3':
+                                       DbgPrint("Microsoft Mouse with 3-buttons detected\n");
+                                       return MOUSE_TYPE_LOGITECH;
+                               case 'Z':
+                                       DbgPrint("Microsoft Wheel Mouse detected\n");
+                                       return MOUSE_TYPE_WHEELZ;
+                               /* case '2': */
+                               default:
+                                       DbgPrint("Microsoft Mouse with 2-buttons detected\n");
+                                       return MOUSE_TYPE_MICROSOFT;
+                       }
+               }
+       }
+
+       return MOUSE_TYPE_NONE;
+}
+
+PDEVICE_OBJECT
+AllocatePointerDevice(PDRIVER_OBJECT DriverObject)
+{
+       PDEVICE_OBJECT DeviceObject;
+       UNICODE_STRING DeviceName;
+       UNICODE_STRING SuffixString;
+       UNICODE_STRING SymlinkName;
+       PDEVICE_EXTENSION DeviceExtension;
+       ULONG Suffix;
+       NTSTATUS Status;
+
+       /* Allocate buffer for full device name */   
+       RtlInitUnicodeString(&DeviceName, NULL);
+       DeviceName.MaximumLength = sizeof(DD_MOUSE_DEVICE_NAME_U) + SUFFIX_MAXIMUM_SIZE + sizeof(UNICODE_NULL);
+       DeviceName.Buffer = ExAllocatePool(PagedPool, DeviceName.MaximumLength);
+       RtlAppendUnicodeToString(&DeviceName, DD_MOUSE_DEVICE_NAME_U);
+
+       /* Allocate buffer for device name suffix */
+       RtlInitUnicodeString(&SuffixString, NULL);
+       SuffixString.MaximumLength = SUFFIX_MAXIMUM_SIZE + sizeof(UNICODE_NULL);
+       SuffixString.Buffer = ExAllocatePool(PagedPool, SuffixString.MaximumLength);
+
+       /* Generate full qualified name with suffix */
+       for (Suffix = 0; Suffix < POINTER_PORTS_MAXIMUM; ++Suffix)
+       {
+               RtlIntegerToUnicodeString(Suffix, 10, &SuffixString);
+               RtlAppendUnicodeToString(&DeviceName, SuffixString.Buffer);
+               Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
+                       &DeviceName, FILE_DEVICE_SERIAL_MOUSE_PORT, 0, TRUE, &DeviceObject);
+               /* Device successfully created, leave the cyclus */
+               if (NT_SUCCESS(Status))
+                       break;
+               DeviceName.Length -= SuffixString.Length;
+       }
+       ExFreePool(DeviceName.Buffer);
+
+       /* Couldn't create device */
+       if (!NT_SUCCESS(Status))
+       {
+               ExFreePool(SuffixString.Buffer);
+               return NULL;
+       }
+
+       DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
+
+#ifdef SERMOUSE_MOUSESYMLINK_SUPPORT
+       /* Create symlink */
+       /* FIXME: Why? FiN 20/08/2003 */
+       RtlInitUnicodeString(&SymlinkName, NULL);
+       SymlinkName.MaximumLength = sizeof(L"\\??\\Mouse") + SUFFIX_MAXIMUM_SIZE + sizeof(UNICODE_NULL);
+       SymlinkName.Buffer = ExAllocatePool(PagedPool, SymlinkName.MaximumLength);
+       RtlAppendUnicodeToString(&SymlinkName, L"\\??\\Mouse");
+       RtlAppendUnicodeToString(&DeviceName, SuffixString.Buffer);
+       IoCreateSymbolicLink(&SymlinkName, &DeviceName);
+#endif
+       ExFreePool(SuffixString.Buffer);
+
+       DeviceExtension = DeviceObject->DeviceExtension;
+       KeInitializeDpc(&DeviceExtension->IsrDpc, (PKDEFERRED_ROUTINE)SerialMouseIsrDpc, DeviceObject);
+
+       return DeviceObject;
+}
+
+BOOLEAN
+InitializeMouse(ULONG Port, ULONG Irq, PDRIVER_OBJECT DriverObject)
+{
+       PDEVICE_EXTENSION DeviceExtension;
+       PDEVICE_OBJECT DeviceObject;
+       ULONG MappedIrq;
+       KIRQL Dirql;
+       KAFFINITY Affinity;
+       ULONG MouseType;
+
+       /* Try to detect mouse on specified port */
+       InitializeSerialPort(Port, 2);
+       MouseType = DetectMicrosoftMouse(Port);
+
+       /* Enable interrupts */
+       WRITE_PORT_UCHAR((PUCHAR)(Port) + 1, 1);
+       ClearMouse(Port);
+
+       /* No mouse, no need to continue */
+       if (MouseType == MOUSE_TYPE_NONE)
+               return FALSE;
+
+    /* Allocate new device */
+    DeviceObject = AllocatePointerDevice(DriverObject);
+    if (!DeviceObject)
+    {
+       DbgPrint("Oops, couldn't creat device object.\n");
+       return FALSE;
+    }
+    DeviceExtension = DeviceObject->DeviceExtension;
+
+    /* Setup device extension structure */
+       DeviceExtension->ActiveQueue = 0;
+       DeviceExtension->MouseType = MouseType;
+       DeviceExtension->MousePort = Port;
+       DeviceExtension->PacketBufferPosition = 0;
+       DeviceExtension->PreviousButtons = 0;
+#ifdef SERMOUSE_QUERYATTRIBUTES_SUPPORT
+       switch (MouseType)
+       {
+               case MOUSE_TYPE_MICROSOFT:
+                       DeviceExtension->AttributesInformation.MouseIdentifier = MOUSE_SERIAL_HARDWARE;
+                       DeviceExtension->AttributesInformation.NumberOfButtons = 2;
+                       break;
+               case MOUSE_TYPE_LOGITECH:
+                       DeviceExtension->AttributesInformation.MouseIdentifier = MOUSE_SERIAL_HARDWARE;
+                       DeviceExtension->AttributesInformation.NumberOfButtons = 3;
+                       break;
+               case MOUSE_TYPE_WHEELZ:
+                       DeviceExtension->AttributesInformation.MouseIdentifier = WHEELMOUSE_SERIAL_HARDWARE;
+                       DeviceExtension->AttributesInformation.NumberOfButtons = 3;
+                       break;
+       };
+       DeviceExtension->AttributesInformation.SampleRate = 40;
+       DeviceExtension->AttributesInformation.InputDataQueueLength = MOUSE_BUFFER_SIZE;
+#endif
+
+       MappedIrq = HalGetInterruptVector(Internal, 0, 0, Irq, &Dirql, &Affinity);
+
+       IoConnectInterrupt(
+               &DeviceExtension->MouseInterrupt, SerialMouseInterruptService,
+               DeviceObject, NULL, MappedIrq, Dirql, Dirql, 0, FALSE,
+               Affinity, FALSE);               
+
+       return TRUE;
+}
+
 NTSTATUS STDCALL
 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
 {
-  PDEVICE_OBJECT DeviceObject;
-  UNICODE_STRING DeviceName;
-  UNICODE_STRING SymlinkName;
-  PDEVICE_EXTENSION DeviceExtension;
-
-  DriverObject->MajorFunction[IRP_MJ_CREATE] = SerialMouseDispatch;
-  DriverObject->MajorFunction[IRP_MJ_CLOSE]  = SerialMouseDispatch;
-  DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = SerialMouseInternalDeviceControl;
-  DriverObject->DriverStartIo                = SerialMouseStartIo;
-
-  RtlInitUnicodeStringFromLiteral(&DeviceName,
-                                  L"\\Device\\Mouse"); /* FIXME: find correct device name */
-  IoCreateDevice(DriverObject,
-         sizeof(DEVICE_EXTENSION),
-         &DeviceName,
-         FILE_DEVICE_SERIAL_MOUSE_PORT,
-         0,
-         TRUE,
-         &DeviceObject);
-  DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
-
-  if(InitializeMouse(DeviceObject) == TRUE)
-  {
-    DbgPrint("Serial Mouse Driver 0.0.5\n");
-  } else {
-    IoDeleteDevice(DeviceObject);
-    DbgPrint("Serial mouse not found.\n");
-    return STATUS_UNSUCCESSFUL;
-  }
-
-  RtlInitUnicodeStringFromLiteral(&SymlinkName,
-                                  L"\\??\\Mouse"); /* FIXME: find correct device name */
-  IoCreateSymbolicLink(&SymlinkName, &DeviceName);
-
-  DeviceExtension = DeviceObject->DeviceExtension;
-  KeInitializeDpc(&DeviceExtension->IsrDpc, (PKDEFERRED_ROUTINE)SerialMouseIsrDpc, DeviceObject);
-
-  return(STATUS_SUCCESS);
+       BOOL MouseFound = FALSE;
+
+       DbgPrint("Serial Mouse Driver 0.0.8\n");
+#ifdef SERMOUSE_COM1_SUPPORT
+       DbgPrint("Trying to find mouse on COM1\n");
+       MouseFound |= InitializeMouse(MOUSE_PORT_COM1, MOUSE_IRQ_COM1, DriverObject);
+#endif
+#ifdef SERMOUSE_COM2_SUPPORT
+       DbgPrint("Trying to find mouse on COM2\n");
+       MouseFound |= InitializeMouse(MOUSE_PORT_COM2, MOUSE_IRQ_COM2, DriverObject);
+#endif
+
+       if (!MouseFound)
+       {
+               DbgPrint("No serial mouse found.\n");
+               return STATUS_UNSUCCESSFUL;
+       }
+
+       DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)SerialMouseDispatch;
+       DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)SerialMouseDispatch;
+       DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = (PDRIVER_DISPATCH)SerialMouseInternalDeviceControl;
+       DriverObject->DriverStartIo = SerialMouseStartIo;
+
+       return STATUS_SUCCESS;
 }