branch update for HEAD-2003050101
[reactos.git] / drivers / input / sermouse / sermouse.c
1 /*
2  * Mouse driver 0.0.6
3  * Written by Jason Filby (jasonfilby@yahoo.com)
4  * For ReactOS (www.reactos.com)
5  *
6  * Note: The serial.o driver must be loaded before loading this driver
7  *
8  * Known Limitations:
9  * Only supports Microsoft mice on COM port 1
10  *
11  * Following information obtained from Tomi Engdahl (then@delta.hut.fi),
12  * http://www.hut.fi/~then/mytexts/mouse.html
13  *
14  * Microsoft serial mouse
15  *
16  *   Serial data parameters:
17  *     1200bps, 7 databits, 1 stop-bit
18  *
19  *   Data packet format:
20  *     Data packet is 3 byte packet. It is send to the computer every time mouse
21  *     state changes (mouse moves or keys are pressed/released). 
22  *         D7      D6      D5      D4      D3      D2      D1      D0
23  *     1.  X       1       LB      RB      Y7      Y6      X7      X6
24  *     2.  X       0       X5      X4      X3      X2      X1      X0      
25  *     3.  X       0       Y5      Y4      Y3      Y2      Y1      Y0
26  *
27  *     Note: The bit marked with X is 0 if the mouse received with 7 databits
28  *     and 2 stop bits format. It is also possible to use 8 databits and 1 stop
29  *     bit format for receiving. In this case X gets value 1. The safest thing
30  *     to get everything working is to use 7 databits and 1 stopbit when
31  *     receiving mouse information (and if you are making mouse then send out
32  *     7 databits and 2 stop bits). 
33  *     The byte marked with 1. is send first, then the others. The bit D6 in
34  *     the first byte is used for syncronizing the software to mouse packets
35  *     if it goes out of sync. 
36  *
37  *      LB is the state of the left button (1 means pressed down)
38  *      RB is the state of the right button (1 means pressed down)
39  *      X7-X0 movement in X direction since last packet (signed byte)
40  *      Y7-Y0 movement in Y direction since last packet (signed byte)
41  *
42  *    Mouse identification
43  *      When DTR line is toggled, mouse should send one data byte containing
44  *      letter 'M' (ascii 77).
45  *
46  *
47  * Logitech serial mouse
48  *
49  *   Logitech uses the Microsoft serial mouse protocol in their mouses (for
50  *   example Logitech Pilot mouse and others). The origianal protocol supports
51  *   only two buttons, but logitech as added third button to some of their
52  *   mouse models. To make this possible logitech has made one extension to
53  *   the protocol. 
54  *   I have not seen any documentation about the exact documents, but here is
55  *   what I have found out: The information of the third button state is sent
56  *   using one extra byte which is send after the normal packet when needed.
57  *   Value 32 (dec) is sent every time when the center button is pressed down.
58  *   It is also sent every time with the data packet when center button is kept
59  *   down and the mouse data packet is sent for other reasons. When center
60  *   button is released, the mouse sends the normal data packet followed by
61  *   data bythe which has value 0 (dec). As you can see the extra data byte
62  *   is sent only when you mess with the center button.
63  *
64  *
65  * Mouse systems mouse
66  *
67  *   Serial data parameters:
68  *     1200bps, 8 databits, 1 stop-bit
69  *
70  *   Data packet format:
71  *          D7      D6      D5      D4      D3      D2      D1      D0
72  *     1.   1       0       0       0       0       LB      CB      RB
73  *     2.   X7      X6      X5      X4      X3      X2      X1      X0
74  *     3.   Y7      Y6      Y5      Y4      Y3      Y4      Y1      Y0
75  *     4.   X7'     X6'     X5'     X4'     X3'     X2'     X1'     X0'
76  *     5.   Y7'     Y6'     Y5'     Y4'     Y3'     Y4'     Y1'     Y0'
77  *
78  *     LB is left button state (0 = pressed, 1 = released)
79  *     CB is center button state (0 = pressed, 1 = released)
80  *     RB is right button state (0 = pressed, 1 = released)
81  *     X7-X0 movement in X direction since last packet in signed byte 
82  *           format (-128..+127), positive direction right
83  *     Y7-Y0 movement in Y direction since last packet in signed byte 
84  *           format (-128..+127), positive direction up
85  *     X7'-X0' movement in X direction since sending of X7-X0 packet in
86  *             signed byte format (-128..+127), positive direction right
87  *     Y7'-Y0' movement in Y direction since sending of Y7-Y0 packet in
88  *             signed byte format (-128..+127), positive direction up
89  *
90  *     The last two bytes in the packet (bytes 4 and 5) contains information
91  *     about movement data changes which have occured after data bytes 2 and 3
92  *     have been sent.
93  *
94  */
95
96 #include <ddk/ntddk.h>
97 #include <ddk/ntddmou.h>
98
99 #define MOUSE_IRQ_COM1  4
100 #define MOUSE_IRQ_COM2  3
101
102 #define MOUSE_PORT_COM1 0x3f8
103 #define MOUSE_PORT_COM2 0x2f8
104
105 typedef struct _DEVICE_EXTENSION {
106   PDEVICE_OBJECT DeviceObject;
107   ULONG ActiveQueue;
108   ULONG InputDataCount[2];
109   MOUSE_INPUT_DATA MouseInputData[2][MOUSE_BUFFER_SIZE];
110   CLASS_INFORMATION ClassInformation;
111
112   PKINTERRUPT MouseInterrupt;
113   KDPC IsrDpc;
114 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
115
116 static unsigned int MOUSE_IRQ = MOUSE_IRQ_COM1;
117 static unsigned int MOUSE_COM = MOUSE_PORT_COM1;
118
119 static unsigned int     bytepos=0, coordinate;
120 static unsigned char    mpacket[3];
121 static unsigned char    mouse_button1, mouse_button2;
122
123 // Previous button state
124 static ULONG PreviousButtons = 0;
125
126 BOOLEAN STDCALL
127 microsoft_mouse_handler(IN PKINTERRUPT Interrupt, PVOID ServiceContext)
128 {
129   PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
130   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
131   PMOUSE_INPUT_DATA Input;
132   ULONG Queue, ButtonsDiff;
133   unsigned int mbyte;
134   int change_x;
135   int change_y;
136   UCHAR InterruptId = READ_PORT_UCHAR((PUCHAR)MOUSE_COM + 2);
137
138   /* Is the interrupt for us? */
139   if (0 != (InterruptId & 0x01))
140   {
141     return FALSE;
142   }
143
144   /* Not a Receive Data Available interrupt? */
145   if (0 == (InterruptId & 0x04))
146   {
147     return TRUE;
148   }
149
150   /* Read all available data and process */
151   while (0 != (READ_PORT_UCHAR((PUCHAR)MOUSE_COM + 5) & 0x01))
152     {
153     mbyte = READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
154
155     /* Synchronize */
156     if (0x40 == (mbyte & 0x40))
157       bytepos=0;
158
159     mpacket[bytepos] = (mbyte & 0x7f);
160     bytepos++;
161
162     /* Process packet if complete */
163     if (3 == bytepos)
164     {
165       /* Set local variables for DeviceObject and DeviceExtension */
166       DeviceObject = (PDEVICE_OBJECT)ServiceContext;
167       DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
168       Queue = DeviceExtension->ActiveQueue % 2;
169
170       /* Prevent buffer overflow */
171       if (DeviceExtension->InputDataCount[Queue] == MOUSE_BUFFER_SIZE)
172       {
173         continue;
174       }
175
176       Input = &DeviceExtension->MouseInputData[Queue]
177               [DeviceExtension->InputDataCount[Queue]];
178
179       /* Retrieve change in x and y from packet */
180       change_x = (int)(signed char)((mpacket[0] & 0x03) << 6) + mpacket[1];
181       change_y = (int)(signed char)((mpacket[0] & 0x0c) << 4) + mpacket[2];
182
183       /* Some mice need this */
184       if (1 == coordinate)
185       {
186         change_x-=128;
187         change_y-=128;
188       }
189
190 #if 0
191       /* Change to signed */
192       if (128 <= change_x)
193       {
194         change_x = change_x - 256;
195       }
196       if (128 <= change_y)
197       {
198         change_y = change_y - 256;
199       }
200 #endif
201
202       Input->LastX = 2 * change_x;
203       Input->LastY = - 3 * change_y;
204
205       /* Retrieve mouse button status from packet */
206       mouse_button1 = mpacket[0] & 0x20;
207       mouse_button2 = mpacket[0] & 0x10;
208     
209       /* Determine the current state of the buttons */
210       Input->RawButtons = mouse_button1 + mouse_button2;
211     
212       /* Determine ButtonFlags */
213       Input->ButtonFlags = 0;
214       ButtonsDiff = PreviousButtons ^ Input->RawButtons;
215
216       if (0 != (ButtonsDiff & 0x20))
217       {
218         if (0 != (Input->RawButtons & 0x20))
219         {
220           Input->ButtonFlags |= MOUSE_BUTTON_1_DOWN;
221         }
222         else
223         {
224           Input->ButtonFlags |= MOUSE_BUTTON_1_UP;
225         }
226       }
227
228       if (0 != (ButtonsDiff & 0x10))
229       {
230         if (0 != (Input->RawButtons & 0x10))
231         {
232           Input->ButtonFlags |= MOUSE_BUTTON_2_DOWN;
233         }
234         else
235         {
236           Input->ButtonFlags |= MOUSE_BUTTON_2_UP;
237         }
238       }
239
240       bytepos=0;
241     
242       /* Send the Input data to the Mouse Class driver */
243       DeviceExtension->InputDataCount[Queue]++;
244       KeInsertQueueDpc(&DeviceExtension->IsrDpc, DeviceObject->CurrentIrp, NULL);
245
246       /* Copy RawButtons to Previous Buttons for Input */
247       PreviousButtons = Input->RawButtons;
248     }
249   }
250
251   return TRUE;
252 }
253
254 void InitializeMouseHardware(unsigned int mtype)
255 {
256   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 3, 0x80);  /* set DLAB on   */
257   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM,     0x60);  /* speed LO byte */
258   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 1, 0);     /* speed HI byte */
259   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 3, mtype); /* 2=MS Mouse; 3=Mouse systems mouse */
260   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 1, 0);     /* set comm and DLAB to 0 */
261   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM + 4, 0x09);  /* DR int enable */
262
263   (void) READ_PORT_UCHAR((PUCHAR)MOUSE_COM+5);    /* clear error bits */
264 }
265
266 int DetMicrosoft(void)
267 {
268   char tmp, ind;
269   int buttons=0, i, timeout=250;
270   LARGE_INTEGER Timeout;
271
272   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM+4, 0x0b);
273   tmp=READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
274
275   /* Check the first four bytes for signs that this is an MS mouse */
276   for(i=0; i<4; i++) {
277     while(((READ_PORT_UCHAR((PUCHAR)MOUSE_COM+5) & 1)==0) && (timeout>0))
278     {
279       Timeout.QuadPart = 1;
280       KeDelayExecutionThread (KernelMode, FALSE, &Timeout);
281       timeout--;
282     }
283     ind=READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
284     if(ind==0x33) buttons=3;
285     if(ind==0x4d) buttons=2;
286   }
287
288   return buttons;
289 }
290
291 int CheckMouseType(unsigned int mtype)
292 {
293   unsigned int retval=0;
294
295   InitializeMouseHardware(mtype);
296   if(mtype==2) retval=DetMicrosoft();
297   if(mtype==3) {
298     WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM+4, 11);
299     retval=3;
300   }
301   WRITE_PORT_UCHAR((PUCHAR)MOUSE_COM+1, 1);
302
303   return retval;
304 }
305
306 void ClearMouse(void)
307 {
308   /* Waits until the mouse calms down but also quits out after a while
309    * in case some destructive user wants to keep moving the mouse
310    * before we're done */
311
312   unsigned int restarts=0, i;
313   for (i=0; i<60000; i++)
314   {
315     unsigned temp=READ_PORT_UCHAR((PUCHAR)MOUSE_COM);
316     if(temp!=0) {
317       restarts++;
318       if(restarts<300000) {
319         i=0;
320       } else
321       {
322         i=60000;
323       }
324     }
325   }
326 }
327
328 BOOLEAN InitializeMouse(PDEVICE_OBJECT DeviceObject)
329 {
330   int mbuttons=0, gotmouse=0;
331   PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
332   ULONG MappedIrq;
333   KIRQL Dirql;
334   KAFFINITY Affinity;
335
336   /* Check for Microsoft mouse (2 buttons) */
337   if(CheckMouseType(2)!=0)
338   {
339     gotmouse=1;
340     DbgPrint("Microsoft Mouse Detected\n");
341     ClearMouse();
342     coordinate=0;
343   }
344
345   /* Check for Microsoft Systems mouse (3 buttons) */
346   if(gotmouse==0) {
347     if(CheckMouseType(3)!=0)
348     {
349     gotmouse=1;
350     DbgPrint("Mouse Systems Mouse Detected\n");
351     ClearMouse();
352     coordinate=1;
353     }
354   }
355
356   if(gotmouse==0) return FALSE;
357
358   DeviceExtension->ActiveQueue    = 0;
359   MappedIrq = HalGetInterruptVector(Internal, 0, 0, MOUSE_IRQ, &Dirql, &Affinity);
360
361   IoConnectInterrupt(&DeviceExtension->MouseInterrupt, microsoft_mouse_handler,
362                      DeviceObject, NULL, MappedIrq, Dirql, Dirql, 0, FALSE,
363                      Affinity, FALSE);
364
365   return TRUE;
366 }
367
368 VOID SerialMouseInitializeDataQueue(PVOID Context)
369 {
370 }
371
372 BOOLEAN STDCALL
373 MouseSynchronizeRoutine(PVOID Context)
374 {
375    PIRP Irp = (PIRP)Context;
376    PMOUSE_INPUT_DATA rec  = (PMOUSE_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
377    PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
378    ULONG NrToRead         = stk->Parameters.Read.Length/sizeof(MOUSE_INPUT_DATA);
379    int i;
380
381    if ((stk->Parameters.Read.Length/sizeof(MOUSE_INPUT_DATA))==NrToRead)
382    {
383       return(TRUE);
384    }
385
386    return(FALSE);
387 }
388
389 VOID STDCALL
390 SerialMouseStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
391 {
392    PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
393
394    if (KeSynchronizeExecution(DeviceExtension->MouseInterrupt, MouseSynchronizeRoutine, Irp))
395      {
396         Irp->IoStatus.Status = STATUS_SUCCESS;
397         Irp->IoStatus.Information = 0;
398         IoCompleteRequest(Irp, IO_NO_INCREMENT);
399         IoStartNextPacket(DeviceObject, FALSE);
400      }
401 }
402
403 NTSTATUS STDCALL
404 SerialMouseInternalDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
405 {
406    PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
407    PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
408    NTSTATUS status;
409
410    switch(Stack->Parameters.DeviceIoControl.IoControlCode)
411    {
412       case IOCTL_INTERNAL_MOUSE_CONNECT:
413
414          DeviceExtension->ClassInformation =
415             *((PCLASS_INFORMATION)Stack->Parameters.DeviceIoControl.Type3InputBuffer);
416
417          /* Reinitialize the port input data queue synchronously */
418          KeSynchronizeExecution(DeviceExtension->MouseInterrupt,
419             (PKSYNCHRONIZE_ROUTINE)SerialMouseInitializeDataQueue, DeviceExtension);
420
421          status = STATUS_SUCCESS;
422          break;
423
424       default:
425          status = STATUS_INVALID_DEVICE_REQUEST;
426          break;
427    }
428
429    Irp->IoStatus.Status = status;
430    if (status == STATUS_PENDING) {
431       IoMarkIrpPending(Irp);
432       IoStartPacket(DeviceObject, Irp, NULL, NULL);
433    } else {
434       IoCompleteRequest(Irp, IO_NO_INCREMENT);
435    }
436
437    return status;
438 }
439
440 NTSTATUS STDCALL
441 SerialMouseDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
442 {
443    PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
444    NTSTATUS Status;
445    static BOOLEAN AlreadyOpened = FALSE;
446
447    switch (stk->MajorFunction)
448      {
449       case IRP_MJ_CREATE:
450         if (AlreadyOpened == TRUE)
451           {
452              Status = STATUS_SUCCESS;
453           }
454         else
455           {
456              Status = STATUS_SUCCESS;
457              AlreadyOpened = TRUE;
458           }
459         break;
460         
461       case IRP_MJ_CLOSE:
462         Status = STATUS_SUCCESS;
463         break;
464
465       default:
466         DbgPrint("NOT IMPLEMENTED\n");
467         Status = STATUS_NOT_IMPLEMENTED;
468         break;
469      }
470
471    if (Status==STATUS_PENDING)
472      {
473         IoMarkIrpPending(Irp);
474      }
475    else
476      {
477         Irp->IoStatus.Status = Status;
478         Irp->IoStatus.Information = 0;
479         IoCompleteRequest(Irp,IO_NO_INCREMENT);
480      }
481    return(Status);
482 }
483
484 VOID SerialMouseIsrDpc(PKDPC Dpc, PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
485 {
486    PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
487    ULONG Queue;
488
489    Queue = DeviceExtension->ActiveQueue % 2;
490    InterlockedIncrement(&DeviceExtension->ActiveQueue);
491    (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassInformation.CallBack)(
492                         DeviceExtension->ClassInformation.DeviceObject,
493                         DeviceExtension->MouseInputData[Queue],
494                         NULL,
495                         &DeviceExtension->InputDataCount[Queue]);
496
497    DeviceExtension->InputDataCount[Queue] = 0;
498 }
499
500 NTSTATUS STDCALL
501 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
502 {
503   PDEVICE_OBJECT DeviceObject;
504   UNICODE_STRING DeviceName;
505   UNICODE_STRING SymlinkName;
506   PDEVICE_EXTENSION DeviceExtension;
507
508   DriverObject->MajorFunction[IRP_MJ_CREATE] = SerialMouseDispatch;
509   DriverObject->MajorFunction[IRP_MJ_CLOSE]  = SerialMouseDispatch;
510   DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = SerialMouseInternalDeviceControl;
511   DriverObject->DriverStartIo                = SerialMouseStartIo;
512
513   RtlInitUnicodeStringFromLiteral(&DeviceName,
514                                   L"\\Device\\Mouse"); /* FIXME: find correct device name */
515   IoCreateDevice(DriverObject,
516           sizeof(DEVICE_EXTENSION),
517           &DeviceName,
518           FILE_DEVICE_SERIAL_MOUSE_PORT,
519           0,
520           TRUE,
521           &DeviceObject);
522   DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
523
524   if(InitializeMouse(DeviceObject) == TRUE)
525   {
526     DbgPrint("Serial Mouse Driver 0.0.5\n");
527   } else {
528     IoDeleteDevice(DeviceObject);
529     DbgPrint("Serial mouse not found.\n");
530     return STATUS_UNSUCCESSFUL;
531   }
532
533   RtlInitUnicodeStringFromLiteral(&SymlinkName,
534                                   L"\\??\\Mouse"); /* FIXME: find correct device name */
535   IoCreateSymbolicLink(&SymlinkName, &DeviceName);
536
537   DeviceExtension = DeviceObject->DeviceExtension;
538   KeInitializeDpc(&DeviceExtension->IsrDpc, (PKDEFERRED_ROUTINE)SerialMouseIsrDpc, DeviceObject);
539
540   return(STATUS_SUCCESS);
541 }