Documented GVF_SAVE_VAR alongside other flags, and removed a query/doubt
[AROS.git] / rom / hidds / serialmouse / drv_serial.c
blob3e73aa17f6dbb7a6e52cfd9507a6234102464052
1 /*
2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: COM mouse driver.
6 Lang: English.
7 */
9 #include <proto/exec.h>
10 #include <proto/utility.h>
11 #include <proto/oop.h>
12 #include <oop/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>
23 #include <stdio.h>
24 #include <strings.h>
26 #include "mouse.h"
28 #define DEBUG 0
29 #include <aros/debug.h>
31 #undef HiddMouseAB
32 #define HiddMouseAB (MSD(cl)->hiddMouseAB)
34 /* defines for buttonstate */
36 #define LEFT_BUTTON 1
37 #define RIGHT_BUTTON 2
38 #define MIDDLE_BUTTON 4
40 /* Prototypes */
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)
57 ULONG ret;
58 ULONG prok = TIMER_RPROK;
59 asm volatile("movl $0,%%eax; divl %2":"=a"(ret):"d"(usec),"m"(prok));
60 return ret;
63 void mouse_usleep(LONG usec)
65 int oldtick, tick;
66 usec = usec2tick(usec);
68 outb(0x80, 0x43);
69 oldtick = inb(0x42);
70 oldtick += inb(0x42) << 8;
72 while (usec > 0)
74 outb(0x80, 0x43);
75 tick = inb(0x42);
76 tick += inb(0x42) << 8;
78 usec -= (oldtick - tick);
79 if (tick > oldtick) usec -= 0x10000;
80 oldtick = tick;
84 /***** Test procedure ***********************************************/
86 int test_mouse_serial(OOP_Class *cl, OOP_Object *o)
88 struct mouse_data *data = OOP_INST_DATA(cl, o);
89 int i=0;
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
99 for mouse.
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 */
111 if (data->rx)
113 for (i=0; i<4; i++)
115 /* Alloc New unit for us */
116 data->unit = HIDD_Serial_NewUnit(data->serial, i);
117 if (data->unit != NULL)
119 int proto;
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;
131 switch (proto)
133 case P_MS:
134 D(bug("Mouse: protocol: MicroSoft\n"));
135 break;
136 case P_LOGI:
137 D(bug("Mouse: protocol: Logitech\n"));
138 break;
139 case P_LOGIMAN:
140 D(bug("Mouse: protocol: Logitech MouseMan\n"));
141 break;
142 default:
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 */
148 else
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 /******************************************************************/
178 #undef SysBase
179 #define SysBase (*(struct ExecBase **)4L)
181 /* serial PnP ID string */
182 typedef struct {
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... */
190 int nserial;
191 int nclass;
192 int ncompat;
193 int ndescription;
194 } pnpid_t;
196 /* symbol table entry */
197 typedef struct {
198 char *name;
199 int val;
200 } symtab_t;
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 */
239 { NULL, -1 },
242 int mouse_pnpgets(struct mouse_data *data, OOP_Object *unit, char *buf)
244 int i,tmpavail;
245 char c;
247 struct TagItem stags[] = {
248 { TAG_DATALENGTH, 7 },
249 { TAG_STOP_BITS, 1 },
250 { TAG_PARITY_OFF, 1 },
251 { TAG_DONE, 0 }};
253 struct TagItem mcr[] = {
254 { TAG_SET_MCR, 0 },
255 { TAG_DONE, 0 }};
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);
273 if (!tmpavail)
274 goto connect_idle;
276 /* Collect PnP COM device ID */
277 i = 0;
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)))
286 buf[0] = c;
287 break;
289 if ((c == 0x08) || (c == 0x28)) /* Begin ID */
291 D(bug("Mouse: yeah, we got a begin ID: %lx\n", c));
292 buf[i++] = c;
293 break;
297 /* we haven't seen `Begin ID' in time... */
298 if(i <= 0)
300 if(buf[0] != 0)
301 return 1;
303 goto connect_idle;
306 ++c; /* make it `End ID' */
307 for (;;)
309 if (!mouse_Select(data, 300000))
310 break;
312 mouse_GetFromRing(data, &buf[i]);
313 if (buf[i++] == c) /* End ID */
314 break;
315 if (i >= 256)
316 break;
319 if (buf[i - 1] != c)
320 goto connect_idle;
322 return i;
324 connect_idle:
325 D(bug("connect_idle\n"));
326 return 0;
329 static int mouse_pnpparse(pnpid_t *id, char *buf, int len)
331 char s[3];
332 int offset;
333 int sum = 0;
334 int i, j;
336 id->revision = 0;
337 id->eisaid = NULL;
338 id->serial = NULL;
339 id->class = NULL;
340 id->compat = NULL;
341 id->description = NULL;
342 id->neisaid = 0;
343 id->nserial = 0;
344 id->nclass = 0;
345 id->ncompat = 0;
346 id->ndescription = 0;
348 offset = 0x28 - buf[0];
350 /* calculate checksum */
351 for (i = 0; i < len - 3; ++i)
353 sum += buf[i];
354 buf[i] += offset;
356 sum += buf[len - 1];
357 for (; i < len; ++i)
358 buf[i] += offset;
359 // D(bug("Mouse: PnP ID string: '%*.*s'\n", len, len, buf));
361 D(bug("Mouse: PnP ID string: '%s'\n", buf));
363 /* revision */
364 buf[1] -= offset;
365 buf[2] -= offset;
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];
371 id->neisaid = 7;
373 D(bug("Mouse: EISA vendor/product ID: %07s\n", id->eisaid));
375 /* option strings */
376 i = 10;
377 if (buf[i] == '\\')
379 /* device serial # */
380 for (j = ++i; i < len; ++i)
382 if (buf[i] == '\\')
383 break;
385 if (i >= len)
386 i -= 3;
387 if (i - j == 8)
389 id->serial = &buf[j];
390 id->nserial = 8;
393 if (buf[i] == '\\')
395 /* PnP class */
396 for (j = ++i; i < len; ++i)
398 if (buf[i] == '\\')
399 break;
401 if (i >= len)
402 i -= 3;
403 if (i > j + 1)
405 id->class = &buf[j];
406 id->nclass = i - j;
410 if (buf[i] == '\\')
412 /* compatible driver */
413 for (j = ++i; i < len; ++i)
415 if (buf[i] == '\\')
416 break;
419 * PnP COM spec prior to v0.96 allowed '*' in this field,
420 * it's not allowed now; just igore it.
422 if (buf[j] == '*')
423 ++j;
424 if (i >= len)
425 i -= 3;
426 if (i > j + 1)
428 id->compat = &buf[j];
429 id->ncompat = i - j;
431 D(bug("Mouse: compat: %d\n", id->compat));
434 if (buf[i] == '\\')
436 /* product description */
437 for (j = ++i; i < len; ++i)
439 if (buf[i] == ';')
440 break;
442 if (i >= len)
443 i -= 3;
444 if (i > j + 1)
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)
462 return TRUE;
465 /* name/val mapping */
467 static symtab_t *gettoken(const symtab_t *tab, const char *s, int len)
469 int i;
471 for (i = 0; tab[i].name != NULL; ++i)
473 if (strncmp(tab[i].name, s, len) == 0)
474 break;
476 return (symtab_t *)&tab[i];
479 static symtab_t *mouse_pnpproto(pnpid_t *id)
481 symtab_t *t;
482 int i, j;
484 if (id->nclass > 0)
485 if (strncmp(id->class, "MOUSE", id->nclass) != 0)
486 /* this is not a mouse! */
487 return NULL;
489 if (id->neisaid > 0)
491 t = gettoken(pnpprod, id->eisaid, id->neisaid);
492 if (t->val != -1)
493 return t;
497 * The 'Compatible drivers' field may contain more than one
498 * ID separated by ','.
500 if (id->ncompat <= 0)
501 return NULL;
502 for (i = 0; i < id->ncompat; ++i)
504 for (j = i; id->compat[i] != ','; ++i)
505 if (i >= id->ncompat)
506 break;
507 if (i > j)
509 t = gettoken(pnpprod, id->compat + j, i - j);
510 if (t->val != -1)
511 return t;
515 return NULL;
518 int mouse_DetectPNP(struct mouse_data *data, OOP_Object *unit)
520 char buf[256];
521 int len;
522 pnpid_t pnpid;
523 symtab_t *t = NULL;
525 len = mouse_pnpgets(data, unit, buf);
527 if (len == 1)
529 if(buf[0] == 77)
530 return 0;
531 if(buf[0] == 79)
532 return 1;
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 */
538 return -1;
540 else if(len > 1)
542 if(!mouse_pnpparse(&pnpid, buf, len))
544 // theowl: !HACK! Even though PnP failed, check for a non-PnP serial mouse.
545 if(buf[0] == 77)
546 return 0;
548 return -1;
550 if ((t = mouse_pnpproto(&pnpid)) == NULL)
551 return -1;
553 else if(len < 1)
554 return -1;
556 D(bug("Mouse: protocol: %d\n", t ? t->val : -1));
558 return (t->val);
561 ULONG mouse_RingHandler(UBYTE *buf, ULONG len, ULONG unit, struct mouse_data *data)
563 struct Ring *r = data->rx;
565 while (len--)
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);
575 return 0;
578 void handle_events(UBYTE proto, struct mouse_data *data)
580 // static UBYTE inbuf[3];
581 struct pHidd_Mouse_Event *e = &data->event;
582 UWORD buttonstate;
583 char c;
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];
603 #if 0
604 if (length)
606 inbuf[2] = *data++;
607 length--;
609 else return 0;
610 #endif
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;
626 D(bug("event 1\n"));
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"));
631 if (e->x || e->y)
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);
641 D(bug("event 2\n"));
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);
653 D(bug("event 3\n"));
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);
663 D(bug("event 4\n"));
665 data->buttonstate = buttonstate;
671 * Check whether there is some data in ring. Return nozero value if there
672 * is anything to get
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;
700 return 1;
703 return 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)
714 int step;
715 int avail = 0;
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
722 * in several steps
724 step = (usec > 50000) ? 50000 : usec;
725 mouse_usleep(step);
726 avail = r->top - r->ptr;
728 /* Decrease wait counter */
729 usec -= step;
731 avail = r->top - r->ptr;
732 return avail;