2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
5 Desc: COM mouse driver.
9 #include <proto/exec.h>
10 #include <proto/utility.h>
11 #include <proto/oop.h>
14 #include <exec/alerts.h>
15 #include <exec/memory.h>
17 #include <hidd/hidd.h>
18 #include <hidd/mouse.h>
19 #include <hidd/serial.h>
21 #include <devices/inputevent.h>
29 #include <aros/debug.h>
32 #define HiddMouseAB (MSD(cl)->hiddMouseAB)
34 /* defines for buttonstate */
37 #define RIGHT_BUTTON 2
38 #define MIDDLE_BUTTON 4
42 ULONG
mouse_RingHandler(UBYTE
*, ULONG
, ULONG
, struct mouse_data
*);
43 int mouse_CheckRing(struct mouse_data
*);
44 int mouse_GetFromRing(struct mouse_data
*, char *);
45 int mouse_Select(struct mouse_data
*, ULONG
);
46 void mouse_FlushInput(struct mouse_data
*);
47 int mouse_DetectPNP(struct mouse_data
*, OOP_Object
*);
48 void handle_events(UBYTE proto
, struct mouse_data
*);
50 /* mouse_usleep - sleep for usec microseconds */
51 /* FIXME: Incompatible with BOCHS busy loop! Change to precise timer.device! */
53 #define TIMER_RPROK 3599597124UL
55 static ULONG
usec2tick(ULONG usec
)
58 ULONG prok
= TIMER_RPROK
;
59 asm volatile("movl $0,%%eax; divl %2":"=a"(ret
):"d"(usec
),"m"(prok
));
63 void mouse_usleep(LONG usec
)
66 usec
= usec2tick(usec
);
70 oldtick
+= inb(0x42) << 8;
76 tick
+= inb(0x42) << 8;
78 usec
-= (oldtick
- tick
);
79 if (tick
> oldtick
) usec
-= 0x10000;
84 /***** Test procedure ***********************************************/
86 int test_mouse_serial(OOP_Class
*cl
, OOP_Object
*o
)
88 struct mouse_data
*data
= OOP_INST_DATA(cl
, o
);
91 data
->shidd
= OpenLibrary("serial.hidd",0);
92 if (data
->shidd
!= NULL
)
94 data
->serial
= OOP_NewObject(NULL
, CLID_Hidd_Serial
, NULL
);
95 if (data
->serial
!= NULL
)
98 As we got serial object, we go now through all units searching
101 Because we don't have timed IO operations yet, we will use busy
102 loops. Be carefull with BOCHS!! It will not understand this!!
104 /* FIXME: Change busy loop for BOCHS!!!! */
106 /* Allocate ring buffer */
108 data
->rx
= AllocMem(sizeof(struct Ring
), MEMF_CLEAR
);
109 data
->mouse_inth_state
= 0; /* initialize to init state */
115 /* Alloc New unit for us */
116 data
->unit
= HIDD_Serial_NewUnit(data
->serial
, i
);
117 if (data
->unit
!= NULL
)
121 D(bug("Checking for mouse on serial port %d\n", i
));
123 /* Install RingBuffer interrupt */
124 HIDD_SerialUnit_Init(data
->unit
, mouse_RingHandler
, data
, NULL
, NULL
);
126 /* Try to get mouse protocol in PnP way */
127 if ((proto
= mouse_DetectPNP(data
, data
->unit
)) >= 0)
129 /* We got protocol */
130 data
->mouse_protocol
= proto
;
134 D(bug("Mouse: protocol: MicroSoft\n"));
137 D(bug("Mouse: protocol: Logitech\n"));
140 D(bug("Mouse: protocol: Logitech MouseMan\n"));
143 D(bug("Mouse: protocol: %d\n", proto
));
145 data
->mouse_inth_state
= 1; /* initialize to event handling state */
146 return 1; /* report the found mouse */
150 D(bug("Mouse: no serial mouse detected!\n"));
151 /* No mouse? Dispose useless unit then */
152 HIDD_Serial_DisposeUnit(data
->serial
, data
->unit
);
156 FreeMem(data
->rx
, sizeof(struct Ring
));
159 /* Found no serial mouse... Dispose serial object */
160 OOP_DisposeObject(data
->serial
);
162 CloseLibrary(data
->shidd
);
164 return 0; /* Report no COM mouse */
167 void dispose_mouse_serial(OOP_Class
*cl
, OOP_Object
*o
) {
168 struct mouse_data
*data
= OOP_INST_DATA(cl
, o
);
170 HIDD_Serial_DisposeUnit(data
->serial
, data
->unit
);
171 FreeMem(data
->rx
, sizeof(struct Ring
));
172 OOP_DisposeObject(data
->serial
);
173 CloseLibrary(data
->shidd
);
176 /******************************************************************/
179 #define SysBase (*(struct ExecBase **)4L)
181 /* serial PnP ID string */
183 int revision
; /* PnP revision, 100 for 1.00 */
184 char *eisaid
; /* EISA ID including mfr ID and product ID */
185 char *serial
; /* serial No, optional */
186 char *class; /* device class, optional */
187 char *compat
; /* list of compatible drivers, optional */
188 char *description
; /* product description, optional */
189 int neisaid
; /* length of the above fields... */
196 /* symbol table entry */
202 static const __attribute__((section(".text"))) symtab_t pnpprod
[] = {
203 { "KML0001", P_THINKING
}, /* Kensignton ThinkingMouse */
204 { "MSH0001", P_IMSERIAL
}, /* MS IntelliMouse */
205 { "MSH0004", P_IMSERIAL
}, /* MS IntelliMouse TrackBall */
206 { "KYEEZ00", P_MS
}, /* Genius EZScroll */
207 { "KYE0001", P_MS
}, /* Genius PnP Mouse */
208 { "KYE0003", P_IMSERIAL
}, /* Genius NetMouse */
209 { "LGI800C", P_IMSERIAL
}, /* Logitech MouseMan (4 button model) */
210 { "LGI8050", P_IMSERIAL
}, /* Logitech MouseMan+ */
211 { "LGI8051", P_IMSERIAL
}, /* Logitech FirstMouse+ */
212 { "LGI8001", P_LOGIMAN
}, /* Logitech serial */
214 { "PNP0F00", P_BM
}, /* MS bus */
215 { "PNP0F01", P_MS
}, /* MS serial */
216 { "PNP0F02", P_BM
}, /* MS InPort */
217 { "PNP0F03", P_PS2
}, /* MS PS/2 */
219 * EzScroll returns PNP0F04 in the compatible device field; but it
220 * doesn't look compatible... XXX
222 { "PNP0F04", P_MSC
}, /* MouseSystems */
223 { "PNP0F05", P_MSC
}, /* MouseSystems */
224 { "PNP0F08", P_LOGIMAN
}, /* Logitech serial */
225 { "PNP0F09", P_MS
}, /* MS BallPoint serial */
226 { "PNP0F0A", P_MS
}, /* MS PnP serial */
227 { "PNP0F0B", P_MS
}, /* MS PnP BallPoint serial */
228 { "PNP0F0C", P_MS
}, /* MS serial comatible */
229 { "PNP0F0D", P_BM
}, /* MS InPort comatible */
230 { "PNP0F0E", P_PS2
}, /* MS PS/2 comatible */
231 { "PNP0F0F", P_MS
}, /* MS BallPoint comatible */
232 { "PNP0F11", P_BM
}, /* MS bus comatible */
233 { "PNP0F12", P_PS2
}, /* Logitech PS/2 */
234 { "PNP0F13", P_PS2
}, /* PS/2 */
235 { "PNP0F15", P_BM
}, /* Logitech bus */
236 { "PNP0F17", P_LOGIMAN
}, /* Logitech serial compat */
237 { "PNP0F18", P_BM
}, /* Logitech bus compatible */
238 { "PNP0F19", P_PS2
}, /* Logitech PS/2 compatible */
242 int mouse_pnpgets(struct mouse_data
*data
, OOP_Object
*unit
, char *buf
)
247 struct TagItem stags
[] = {
248 { TAG_DATALENGTH
, 7 },
249 { TAG_STOP_BITS
, 1 },
250 { TAG_PARITY_OFF
, 1 },
253 struct TagItem mcr
[] = {
257 /* Try to detect mouse according to XF86 sources. */
258 HIDD_SerialUnit_SetBaudrate(unit
, 1200);
259 HIDD_SerialUnit_SetParameters(unit
, stags
);
261 /* Set DTR=1, RTS=0 */
262 // mcr[0].ti_Data = 1;
263 /* Set DTR=0, RTS=0 */
264 HIDD_SerialUnit_SetParameters(unit
, mcr
);
265 mouse_usleep(200000);
267 /* wait for response */
268 mouse_FlushInput(data
);
269 mcr
[0].ti_Data
= 3; /* DTR=1, RTS=1 */
270 HIDD_SerialUnit_SetParameters(unit
, mcr
);
271 /* Try to read data. Mouse has to respond if PNP */
272 tmpavail
= mouse_Select(data
, 300000);
276 /* Collect PnP COM device ID */
278 mouse_usleep(200000); /* the mouse must send `Begin ID' within 200msec */
279 while (mouse_GetFromRing(data
, &c
))
281 D(bug("Mouse: nopnp protocol detection %ld\n", c
));
283 /* we may see "M", or "M3..." before `Begin ID' */
284 if ((i
== 0) && ((c
== 77) || (c
== 79)))
289 if ((c
== 0x08) || (c
== 0x28)) /* Begin ID */
291 D(bug("Mouse: yeah, we got a begin ID: %lx\n", c
));
297 /* we haven't seen `Begin ID' in time... */
306 ++c
; /* make it `End ID' */
309 if (!mouse_Select(data
, 300000))
312 mouse_GetFromRing(data
, &buf
[i
]);
313 if (buf
[i
++] == c
) /* End ID */
325 D(bug("connect_idle\n"));
329 static int mouse_pnpparse(pnpid_t
*id
, char *buf
, int len
)
341 id
->description
= NULL
;
346 id
->ndescription
= 0;
348 offset
= 0x28 - buf
[0];
350 /* calculate checksum */
351 for (i
= 0; i
< len
- 3; ++i
)
359 // D(bug("Mouse: PnP ID string: '%*.*s'\n", len, len, buf));
361 D(bug("Mouse: PnP ID string: '%s'\n", buf
));
366 id
->revision
= ((buf
[1] & 0x3f) << 6) | (buf
[2] & 0x3f);
367 D(bug("Mouse: PnP rev %d.%02d\n", id
->revision
/ 100, id
->revision
% 100));
369 /* EISA vender and product ID */
370 id
->eisaid
= &buf
[3];
373 D(bug("Mouse: EISA vendor/product ID: %07s\n", id
->eisaid
));
379 /* device serial # */
380 for (j
= ++i
; i
< len
; ++i
)
389 id
->serial
= &buf
[j
];
396 for (j
= ++i
; i
< len
; ++i
)
412 /* compatible driver */
413 for (j
= ++i
; i
< len
; ++i
)
419 * PnP COM spec prior to v0.96 allowed '*' in this field,
420 * it's not allowed now; just igore it.
428 id
->compat
= &buf
[j
];
431 D(bug("Mouse: compat: %d\n", id
->compat
));
436 /* product description */
437 for (j
= ++i
; i
< len
; ++i
)
446 id
->description
= &buf
[j
];
447 id
->ndescription
= i
- j
;
449 D(bug("Mouse: product description: %s\n", id
->description
));
452 /* checksum exists if there are any optional fields */
453 if ((id
->nserial
> 0) || (id
->nclass
> 0)
454 || (id
->ncompat
> 0) || (id
->ndescription
> 0))
456 sprintf(s
, "%02X", sum
& 0x0ff);
457 D(bug("Mouse: optional fields ?: %s\n", s
));
458 if (strncmp(s
, &buf
[len
- 3], 2) != 0)
465 /* name/val mapping */
467 static symtab_t
*gettoken(const symtab_t
*tab
, const char *s
, int len
)
471 for (i
= 0; tab
[i
].name
!= NULL
; ++i
)
473 if (strncmp(tab
[i
].name
, s
, len
) == 0)
476 return (symtab_t
*)&tab
[i
];
479 static symtab_t
*mouse_pnpproto(pnpid_t
*id
)
485 if (strncmp(id
->class, "MOUSE", id
->nclass
) != 0)
486 /* this is not a mouse! */
491 t
= gettoken(pnpprod
, id
->eisaid
, id
->neisaid
);
497 * The 'Compatible drivers' field may contain more than one
498 * ID separated by ','.
500 if (id
->ncompat
<= 0)
502 for (i
= 0; i
< id
->ncompat
; ++i
)
504 for (j
= i
; id
->compat
[i
] != ','; ++i
)
505 if (i
>= id
->ncompat
)
509 t
= gettoken(pnpprod
, id
->compat
+ j
, i
- j
);
518 int mouse_DetectPNP(struct mouse_data
*data
, OOP_Object
*unit
)
525 len
= mouse_pnpgets(data
, unit
, buf
);
534 /* stegerg: checkme! Added this return -1, because if this is
535 not there, then below the "return (t->val)" is used to leave
536 the function, with t pointing to random address */
542 if(!mouse_pnpparse(&pnpid
, buf
, len
))
544 // theowl: !HACK! Even though PnP failed, check for a non-PnP serial mouse.
550 if ((t
= mouse_pnpproto(&pnpid
)) == NULL
)
556 D(bug("Mouse: protocol: %d\n", t
? t
->val
: -1));
561 ULONG
mouse_RingHandler(UBYTE
*buf
, ULONG len
, ULONG unit
, struct mouse_data
*data
)
563 struct Ring
*r
= data
->rx
;
567 r
->ring
[r
->top
++] = *buf
++;
569 if (r
->top
>= RingSize
) r
->top
= 0;
572 if(data
->mouse_inth_state
>= 1)
573 handle_events(data
->mouse_protocol
, data
);
578 void handle_events(UBYTE proto
, struct mouse_data
*data
)
580 // static UBYTE inbuf[3];
581 struct pHidd_Mouse_Event
*e
= &data
->event
;
585 D(bug("Mouse: handling events, proto: %ld\n", proto
));
587 while (mouse_GetFromRing(data
, &c
))
589 D(bug("Mouse: handling events, c: %d\n", c
));
591 data
->mouse_data
[data
->mouse_collected_bytes
++] = c
;
593 D(bug("mouse_data: %d, colb: %d\n", data
->mouse_data
[data
->mouse_collected_bytes
],data
->mouse_collected_bytes
));
595 if (data
->mouse_collected_bytes
== 3)
597 data
->mouse_collected_bytes
= 0;
598 while (!(data
->mouse_data
[0] & 0x40))
600 data
->mouse_data
[0] = data
->mouse_data
[1];
601 data
->mouse_data
[1] = data
->mouse_data
[2];
614 microsoft serial mouse protocol:
616 D7 D6 D5 D4 D3 D2 D1 D0
618 1. X 1 LB RB Y7 Y6 X7 X6
619 2. X 0 X5 X4 X3 X2 X1 X0
620 3. X 0 Y5 Y4 Y3 Y2 Y1 Y0
624 //mousedata = (struct mouse_data *)userdata;
627 e
->x
= (char)(((data
->mouse_data
[0] & 0x03) << 6) | (data
->mouse_data
[1] & 0x3f));
628 D(bug("subevent 1\n"));
629 e
->y
= (char)(((data
->mouse_data
[0] & 0x0c) << 4) | (data
->mouse_data
[2] & 0x3f));
630 D(bug("subevent 1\n"));
633 D(bug("subevent 2\n"));
634 e
->button
= vHidd_Mouse_NoButton
;
635 D(bug("subevent 3\n"));
636 e
->type
= vHidd_Mouse_Motion
;
638 D(bug("subevent 4\n"));
639 data
->mouse_callback(data
->callbackdata
, e
);
643 buttonstate
= ((data
->mouse_data
[0] & 0x20) >> 5); /* left button bit goes to bit 0 in button state */
644 buttonstate
|= ((data
->mouse_data
[0] & 0x10) >> 3); /* right button bit goes to bit 1 in button state */
646 if((buttonstate
& LEFT_BUTTON
) != (data
->buttonstate
& LEFT_BUTTON
))
648 e
->button
= vHidd_Mouse_Button1
;
649 e
->type
= (buttonstate
& LEFT_BUTTON
) ? vHidd_Mouse_Press
: vHidd_Mouse_Release
;
651 data
->mouse_callback(data
->callbackdata
, e
);
655 if((buttonstate
& RIGHT_BUTTON
) != (data
->buttonstate
& RIGHT_BUTTON
))
657 e
->button
= vHidd_Mouse_Button2
;
658 e
->type
= (buttonstate
& RIGHT_BUTTON
) ? vHidd_Mouse_Press
: vHidd_Mouse_Release
;
660 data
->mouse_callback(data
->callbackdata
, e
);
665 data
->buttonstate
= buttonstate
;
671 * Check whether there is some data in ring. Return nozero value if there
674 int mouse_CheckRing(struct mouse_data
*data
)
676 return data
->rx
->top
- data
->rx
->ptr
;
680 * Clears ring buffer so it can get new data
682 void mouse_FlushInput(struct mouse_data
*data
)
684 data
->rx
->ptr
= data
->rx
->top
;
688 * Get one byte from ring buffer. Returns 0 if there is nothing to get
690 int mouse_GetFromRing(struct mouse_data
*data
, char *c
)
692 struct Ring
*r
= data
->rx
;
694 if (r
->top
!= r
->ptr
)
696 *c
= r
->ring
[r
->ptr
++];
698 if (r
->ptr
>= RingSize
) r
->ptr
= 0;
707 * Select version for Ring handling.
709 * This functions waits for data present in Ring buffer for usec. Returns
710 * non-zero if there is something in buffer, 0 otherwise.
712 int mouse_Select(struct mouse_data
*data
, ULONG usec
)
716 struct Ring
*r
= data
->rx
;
718 while (usec
&& !avail
)
721 * If we want to wait longer than 50000 usec, then we have to do it
724 step
= (usec
> 50000) ? 50000 : usec
;
726 avail
= r
->top
- r
->ptr
;
728 /* Decrease wait counter */
731 avail
= r
->top
- r
->ptr
;