1 /*
2  * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Kazutaka YOKOTA not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  Kazutaka YOKOTA makes no representations
11  * about the suitability of this software for any purpose.  It is provided
12  * "as is" without express or implied warranty.
13  *
14  * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <xorg-server.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <X11/X.h>
32 #include <X11/Xproto.h>
33 #include "inputstr.h"
34 #include "scrnintstr.h"
35 #include "xf86.h"
36 #include "xf86Priv.h"
37 #include "xf86Xinput.h"
38 #include "xf86_OSproc.h"
39 #include "mouse.h"
40 #include "mousePriv.h"
41 
42 #ifdef MOUSEINITDEBUG
43 # define DEBUG
44 # define EXTMOUSEDEBUG
45 #endif
46 
47 /* serial PnP ID string */
48 typedef struct {
49     int revision;       /* PnP revision, 100 for 1.00 */
50     char *eisaid;       /* EISA ID including mfr ID and product ID */
51     char *serial;       /* serial No, optional */
52     char *class;        /* device class, optional */
53     char *compat;       /* list of compatible drivers, optional */
54     char *description;  /* product description, optional */
55     int neisaid;        /* length of the above fields... */
56     int nserial;
57     int nclass;
58     int ncompat;
59     int ndescription;
60 } pnpid_t;
61 
62 /* symbol table entry */
63 typedef struct {
64     const char *name;
65     MouseProtocolID val;
66 } symtab_t;
67 
68 /* PnP EISA/product IDs */
69 static symtab_t pnpprod[] = {
70     { "KML0001",  PROT_THINKING },      /* Kensington ThinkingMouse */
71     { "MSH0001",  PROT_IMSERIAL },      /* MS IntelliMouse */
72     { "MSH0004",  PROT_IMSERIAL },      /* MS IntelliMouse TrackBall */
73     { "KYEEZ00",  PROT_MS },            /* Genius EZScroll */
74     { "KYE0001",  PROT_MS },            /* Genius PnP Mouse */
75     { "KYE0002",  PROT_MS },            /* MouseSystem (Genius?) SmartScroll */
76     { "KYE0003",  PROT_IMSERIAL },      /* Genius NetMouse */
77     { "KYE0004",  PROT_IMSERIAL },      /* Genius NetScroll+ (serial) */
78     { "LGI800C",  PROT_IMSERIAL },      /* Logitech MouseMan (4 button model) */
79     { "LGI8033",  PROT_IMSERIAL },      /* Logitech Cordless MouseMan Wheel */
80     { "LGI8050",  PROT_IMSERIAL },      /* Logitech MouseMan+ */
81     { "LGI8051",  PROT_IMSERIAL },      /* Logitech FirstMouse+ */
82     { "LGI8001",  PROT_LOGIMAN },       /* Logitech serial */
83     { "A4W0005",  PROT_IMSERIAL },      /* A4 Tech 4D/4D+ Mouse */
84     { "PEC9802",  PROT_IMSERIAL },      /* 8D Scroll Mouse */
85 
86     { "PNP0F00",  PROT_BM },            /* MS bus */
87     { "PNP0F01",  PROT_MS },            /* MS serial */
88     { "PNP0F02",  PROT_BM },            /* MS InPort */
89     { "PNP0F03",  PROT_PS2 },           /* MS PS/2 */
90     /*
91      * EzScroll returns PNP0F04 in the compatible device field; but it
92      * doesn't look compatible... XXX
93      */
94     { "PNP0F04",  PROT_MSC },           /* MouseSystems */
95     { "PNP0F05",  PROT_MSC },           /* MouseSystems */
96 #ifdef notyet
97     { "PNP0F06",  PROT_??? },           /* Genius Mouse */
98     { "PNP0F07",  PROT_??? },           /* Genius Mouse */
99 #endif
100     { "PNP0F08",  PROT_LOGIMAN },       /* Logitech serial */
101     { "PNP0F09",  PROT_MS },            /* MS BallPoint serial */
102     { "PNP0F0A",  PROT_MS },            /* MS PnP serial */
103     { "PNP0F0B",  PROT_MS },            /* MS PnP BallPoint serial */
104     { "PNP0F0C",  PROT_MS },            /* MS serial compatible */
105     { "PNP0F0D",  PROT_BM },            /* MS InPort compatible */
106     { "PNP0F0E",  PROT_PS2 },           /* MS PS/2 compatible */
107     { "PNP0F0F",  PROT_MS },            /* MS BallPoint compatible */
108 #ifdef notyet
109     { "PNP0F10",  PROT_??? },           /* TI QuickPort */
110 #endif
111     { "PNP0F11",  PROT_BM },            /* MS bus compatible */
112     { "PNP0F12",  PROT_PS2 },           /* Logitech PS/2 */
113     { "PNP0F13",  PROT_PS2 },           /* PS/2 */
114 #ifdef notyet
115     { "PNP0F14",  PROT_??? },           /* MS Kids Mouse */
116 #endif
117     { "PNP0F15",  PROT_BM },            /* Logitech bus */
118 #ifdef notyet
119     { "PNP0F16",  PROT_??? },           /* Logitech SWIFT */
120 #endif
121     { "PNP0F17",  PROT_LOGIMAN },       /* Logitech serial compat */
122     { "PNP0F18",  PROT_BM },            /* Logitech bus compatible */
123     { "PNP0F19",  PROT_PS2 },           /* Logitech PS/2 compatible */
124 #ifdef notyet
125     { "PNP0F1A",  PROT_??? },           /* Logitech SWIFT compatible */
126     { "PNP0F1B",  PROT_??? },           /* HP Omnibook */
127     { "PNP0F1C",  PROT_??? },           /* Compaq LTE TrackBall PS/2 */
128     { "PNP0F1D",  PROT_??? },           /* Compaq LTE TrackBall serial */
129     { "PNP0F1E",  PROT_??? },           /* MS Kids Trackball */
130 #endif
131     { NULL,       PROT_UNKNOWN },
132 };
133 
134 static const char *pnpSerial[] = {
135         "BaudRate",     "1200",
136         "DataBits",     "7",
137         "StopBits",     "1",
138         "Parity",       "None",
139         "FlowControl",  "None",
140         "VTime",        "0",
141         "VMin",         "1",
142         NULL
143 };
144 
145 static int pnpgets(InputInfoPtr, char *, Bool *prePNP);
146 static int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
147 static MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf);
148 static symtab_t *pnpproto(pnpid_t *);
149 static symtab_t *gettoken(symtab_t *, char *, int);
150 static MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo);
151 static MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo);
152 
153 static MouseProtocolID
MouseGetSerialPnpProtocol(InputInfoPtr pInfo)154 MouseGetSerialPnpProtocol(InputInfoPtr pInfo)
155 {
156     char buf[256];      /* PnP ID string may be up to 256 bytes long */
157     pnpid_t pnpid;
158     symtab_t *t;
159     int len;
160     Bool prePNP;
161 
162     if ((len = pnpgets(pInfo, buf, &prePNP)) > 0)
163     {
164         if (!prePNP) {
165             if (pnpparse(pInfo, &pnpid, buf, len) &&
166                 (t = pnpproto(&pnpid)) != NULL) {
167                 xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n",
168                             pInfo->name, t->val);
169                 return (t->val);
170             }
171         } else
172             return prepnpparse(pInfo,buf);
173     }
174     return PROT_UNKNOWN;
175 }
176 
177 MouseProtocolID
MouseGetPnpProtocol(InputInfoPtr pInfo)178 MouseGetPnpProtocol(InputInfoPtr pInfo)
179 {
180     MouseDevPtr  pMse = pInfo->private;
181     mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv;
182     MouseProtocolID val;
183     CARD32 last;
184 
185     if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) {
186         if (val == MouseGetSerialPnpProtocol(pInfo))
187             return val;
188     }
189 
190     last = mPriv->pnpLast;
191     mPriv->pnpLast = currentTime.milliseconds;
192 
193     if (last) {
194         if (last - currentTime.milliseconds < 100
195             || (mPriv->disablePnPauto
196                 && (last - currentTime.milliseconds < 10000))) {
197 #ifdef EXTMOUSEDEBUG
198             xf86ErrorF("Mouse: Disabling PnP\n");
199 #endif
200             mPriv->disablePnPauto = TRUE;
201             return PROT_UNKNOWN;
202         }
203     }
204 
205 #ifdef EXTMOUSEDEBUG
206     if (mPriv->disablePnPauto)
207         xf86ErrorF("Mouse: Enabling PnP\n");
208 #endif
209     mPriv->disablePnPauto = FALSE;
210 
211     if (mPriv->soft)
212         return getPs2ProtocolPnP(pInfo);
213     else
214         return probePs2ProtocolPnP(pInfo);
215 }
216 
217 /*
218  * Try to elicit a PnP ID as described in
219  * Microsoft, Hayes: "Plug and Play External COM Device Specification,
220  * rev 1.00", 1995.
221  *
222  * The routine does not fully implement the COM Enumerator as per Section
223  * 2.1 of the document.  In particular, we don't have idle state in which
224  * the driver software monitors the com port for dynamic connection or
225  * removal of a device at the port, because `moused' simply quits if no
226  * device is found.
227  *
228  * In addition, as PnP COM device enumeration procedure slightly has
229  * changed since its first publication, devices which follow earlier
230  * revisions of the above spec. may fail to respond if the rev 1.0
231  * procedure is used. XXX
232  */
233 static int
pnpgets(InputInfoPtr pInfo,char * buf,Bool * prePNP)234 pnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP)
235 {
236     int i;
237     char c;
238     pointer pnpOpts;
239 
240 #if 0
241     /*
242      * This is the procedure described in rev 1.0 of PnP COM device spec.
243      * Unfortunately, some devices which conform to earlier revisions of
244      * the spec gets confused and do not return the ID string...
245      */
246 
247     /* port initialization (2.1.2) */
248     if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
249         return 0;
250     i |= XF86_M_DTR;            /* DTR = 1 */
251     i &= ~XF86_M_RTS;           /* RTS = 0 */
252     if (xf86SetSerialModemState(pInfo->fd, i) == -1)
253         goto disconnect_idle;
254     usleep(200000);
255     if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
256         (i & XF86_M_DSR) == 0)
257         goto disconnect_idle;
258 
259     /* port setup, 1st phase (2.1.3) */
260     pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
261     xf86SetSerial(pInfo->fd, pnpOpts);
262     i = TIOCM_DTR | TIOCM_RTS;  /* DTR = 0, RTS = 0 */
263     xf86SerialModemClearBits(pInfo->fd, i);
264     usleep(200000);
265     i = TIOCM_DTR;              /* DTR = 1, RTS = 0 */
266     xf86SerialModemSetBits(pInfo->fd, i);
267     usleep(200000);
268 
269     /* wait for response, 1st phase (2.1.4) */
270     xf86FlushInput(pInfo->fd);
271     i = TIOCM_RTS;              /* DTR = 1, RTS = 1 */
272     xf86SerialModemSetBits(pInfo->fd, i);
273 
274     /* try to read something */
275     if (xf86WaitForInput(pInfo->fd, 200000) <= 0) {
276 
277         /* port setup, 2nd phase (2.1.5) */
278         i = TIOCM_DTR | TIOCM_RTS;      /* DTR = 0, RTS = 0 */
279         xf86SerialModemClearBits(pInfo->fd, i);
280         usleep(200000);
281 
282         /* wait for response, 2nd phase (2.1.6) */
283         xf86FlushInput(pInfo->fd);
284         i = TIOCM_DTR | TIOCM_RTS;      /* DTR = 1, RTS = 1 */
285         xf86SerialModemSetBits(pInfo->fd, i);
286 
287         /* try to read something */
288         if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
289             goto connect_idle;
290     }
291 #else
292     /*
293      * This is a simplified procedure; it simply toggles RTS.
294      */
295 
296     if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
297         return 0;
298     i |= XF86_M_DTR;            /* DTR = 1 */
299     i &= ~XF86_M_RTS;           /* RTS = 0 */
300     if (xf86SetSerialModemState(pInfo->fd, i) == -1)
301         goto disconnect_idle;
302     usleep(200000);
303 
304     pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
305     xf86SetSerial(pInfo->fd, pnpOpts);
306 
307     /* wait for response */
308     xf86FlushInput(pInfo->fd);
309     i = XF86_M_DTR | XF86_M_RTS;        /* DTR = 1, RTS = 1 */
310     xf86SerialModemSetBits(pInfo->fd, i);
311 
312     /* try to read something */
313     if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
314         goto connect_idle;
315 #endif
316 
317     /* collect PnP COM device ID (2.1.7) */
318     i = 0;
319     *prePNP = FALSE;
320 
321     usleep(200000);     /* the mouse must send `Begin ID' within 200msec */
322     while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
323         /* we may see "M", or "M3..." before `Begin ID' */
324         if (c == 'M')
325             *prePNP = TRUE;
326 
327         if ((c == 0x08) || (c == 0x28)) {       /* Begin ID */
328             *prePNP = FALSE;
329             buf[0] = c;
330             i = 1;
331             break;
332         }
333         if (*prePNP)
334             buf[i++] = c;
335 
336         if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
337             break;
338     }
339     if (i <= 0) {
340         /* we haven't seen `Begin ID' in time... */
341         goto connect_idle;
342     }
343     if (*prePNP)
344         return i;
345 
346     ++c;                        /* make it `End ID' */
347     for (;;) {
348         if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
349             break;
350 
351         xf86ReadSerial(pInfo->fd, &buf[i], 1);
352         if (buf[i++] == c)      /* End ID */
353             break;
354         if (i >= 256)
355             break;
356     }
357     if (buf[i - 1] != c)
358         goto connect_idle;
359     return i;
360 
361     /*
362      * According to PnP spec, we should set DTR = 1 and RTS = 0 while
363      * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
364      * assuming there is something at the port even if it didn't
365      * respond to the PnP enumeration procedure.
366      */
367 disconnect_idle:
368     i = XF86_M_DTR | XF86_M_RTS;                /* DTR = 1, RTS = 1 */
369     xf86SerialModemSetBits(pInfo->fd, i);
370 connect_idle:
371     return 0;
372 }
373 
374 static int
pnpparse(InputInfoPtr pInfo,pnpid_t * id,char * buf,int len)375 pnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len)
376 {
377     char s[3];
378     int offset;
379     int sum = 0;
380     int i, j;
381 
382     id->revision = 0;
383     id->eisaid = NULL;
384     id->serial = NULL;
385     id->class = NULL;
386     id->compat = NULL;
387     id->description = NULL;
388     id->neisaid = 0;
389     id->nserial = 0;
390     id->nclass = 0;
391     id->ncompat = 0;
392     id->ndescription = 0;
393 
394     offset = 0x28 - buf[0];
395 
396     /* calculate checksum */
397     for (i = 0; i < len - 3; ++i) {
398         sum += buf[i];
399         buf[i] += offset;
400     }
401     sum += buf[len - 1];
402     for (; i < len; ++i)
403         buf[i] += offset;
404     xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
405                 len, len, buf);
406 
407     /* revision */
408     buf[1] -= offset;
409     buf[2] -= offset;
410     id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
411     xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name,
412                 id->revision / 100, id->revision % 100);
413 
414     /* EISA vender and product ID */
415     id->eisaid = &buf[3];
416     id->neisaid = 7;
417 
418     /* option strings */
419     i = 10;
420     if (buf[i] == '\\') {
421         /* device serial # */
422         for (j = ++i; i < len; ++i) {
423             if (buf[i] == '\\')
424                 break;
425         }
426         if (i >= len)
427             i -= 3;
428         if (i - j == 8) {
429             id->serial = &buf[j];
430             id->nserial = 8;
431         }
432     }
433     if (buf[i] == '\\') {
434         /* PnP class */
435         for (j = ++i; i < len; ++i) {
436             if (buf[i] == '\\')
437                 break;
438         }
439         if (i >= len)
440             i -= 3;
441         if (i > j + 1) {
442             id->class = &buf[j];
443             id->nclass = i - j;
444         }
445     }
446     if (buf[i] == '\\') {
447         /* compatible driver */
448         for (j = ++i; i < len; ++i) {
449             if (buf[i] == '\\')
450                 break;
451         }
452         /*
453          * PnP COM spec prior to v0.96 allowed '*' in this field,
454          * it's not allowed now; just ignore it.
455          */
456         if (buf[j] == '*')
457             ++j;
458         if (i >= len)
459             i -= 3;
460         if (i > j + 1) {
461             id->compat = &buf[j];
462             id->ncompat = i - j;
463         }
464     }
465     if (buf[i] == '\\') {
466         /* product description */
467         for (j = ++i; i < len; ++i) {
468             if (buf[i] == ';')
469                 break;
470         }
471         if (i >= len)
472             i -= 3;
473         if (i > j + 1) {
474             id->description = &buf[j];
475             id->ndescription = i - j;
476         }
477     }
478 
479     /* checksum exists if there are any optional fields */
480     if ((id->nserial > 0) || (id->nclass > 0)
481         || (id->ncompat > 0) || (id->ndescription > 0)) {
482         xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum);
483         sprintf(s, "%02X", sum & 0x0ff);
484         if (strncmp(s, &buf[len - 3], 2) != 0) {
485 #if 0
486             /*
487              * Checksum error!!
488              * I found some mice do not comply with the PnP COM device
489              * spec regarding checksum... XXX
490              */
491             return FALSE;
492 #endif
493         }
494     }
495 
496     return TRUE;
497 }
498 
499 /* We can only identify MS at the moment */
500 static MouseProtocolID
prepnpparse(InputInfoPtr pInfo,char * buf)501 prepnpparse(InputInfoPtr pInfo, char *buf)
502 {
503     if (buf[0] == 'M' && buf[1] == '3')
504         return PROT_MS;
505     return PROT_UNKNOWN;
506 }
507 
508 
509 static symtab_t *
pnpproto(pnpid_t * id)510 pnpproto(pnpid_t *id)
511 {
512     symtab_t *t;
513     int i, j;
514 
515     if (id->nclass > 0)
516         if (strncmp(id->class, "MOUSE", id->nclass) != 0)
517             /* this is not a mouse! */
518             return NULL;
519 
520     if (id->neisaid > 0) {
521         t = gettoken(pnpprod, id->eisaid, id->neisaid);
522         if (t->val != -1)
523             return t;
524     }
525 
526     /*
527      * The 'Compatible drivers' field may contain more than one
528      * ID separated by ','.
529      */
530     if (id->ncompat <= 0)
531         return NULL;
532     for (i = 0; i < id->ncompat; ++i) {
533         for (j = i; id->compat[i] != ','; ++i)
534             if (i >= id->ncompat)
535                 break;
536         if (i > j) {
537             t = gettoken(pnpprod, id->compat + j, i - j);
538             if (t->val != -1)
539                 return t;
540         }
541     }
542 
543     return NULL;
544 }
545 
546 /* name/val mapping */
547 
548 static symtab_t *
gettoken(symtab_t * tab,char * s,int len)549 gettoken(symtab_t *tab, char *s, int len)
550 {
551     int i;
552 
553     for (i = 0; tab[i].name != NULL; ++i) {
554         if (strncmp(tab[i].name, s, len) == 0)
555             break;
556     }
557     return &tab[i];
558 }
559 
560 /******************* PS/2 PnP probing ****************/
561 
562 static int
readMouse(InputInfoPtr pInfo,unsigned char * u)563 readMouse(InputInfoPtr pInfo, unsigned char *u)
564 {
565 
566     if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
567         return FALSE;
568 
569     xf86ReadSerial(pInfo->fd, u, 1);
570     return TRUE;
571 }
572 
573 static void
ps2DisableWrapMode(InputInfoPtr pInfo)574 ps2DisableWrapMode(InputInfoPtr pInfo)
575 {
576     unsigned char reset_wrap_mode[] = { 0xEC };
577     ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
578 }
579 
580 Bool
ps2SendPacket(InputInfoPtr pInfo,unsigned char * bytes,int len)581 ps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len)
582 {
583     unsigned char c;
584     int i,j;
585 
586 #ifdef DEBUG
587     xf86ErrorF("Ps/2 data package:");
588     for (i = 0; i < len; i++)
589         xf86ErrorF(" %x", *(bytes + i));
590     xf86ErrorF("\n");
591 #endif
592 
593     for (i = 0; i < len; i++) {
594         for (j = 0; j < 10; j++) {
595             xf86WriteSerial(pInfo->fd, bytes + i, 1);
596             usleep(10000);
597             if (!readMouse(pInfo,&c)) {
598 #ifdef DEBUG
599                 xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i));
600 #endif
601                 return FALSE;
602             }
603 #ifdef DEBUG
604             xf86ErrorF("Recieved: 0x%x\n",c);
605 #endif
606             if (c == 0xFA) /* ACK */
607                 break;
608 
609             if (c == 0xFE) /* resend */
610                 continue;
611 
612 
613             if (c == 0xFC) /* error */
614                 return FALSE;
615 
616             /* Some mice accidently enter wrap mode during init */
617             if (c == *(bytes + i)    /* wrap mode */
618                 && (*(bytes + i) != 0xEC)) /* avoid recursion */
619                 ps2DisableWrapMode(pInfo);
620 
621             return FALSE;
622         }
623         if (j == 10)
624             return FALSE;
625     }
626 
627     return TRUE;
628 }
629 
630 static Bool
ps2DisableDataReporting(InputInfoPtr pInfo)631 ps2DisableDataReporting(InputInfoPtr pInfo)
632 {
633     unsigned char packet[] = { 0xF5 };
634     return ps2SendPacket(pInfo, packet, sizeof(packet));
635 }
636 
637 Bool
ps2EnableDataReporting(InputInfoPtr pInfo)638 ps2EnableDataReporting(InputInfoPtr pInfo)
639 {
640     unsigned char packet[] = { 0xF4 };
641     return ps2SendPacket(pInfo, packet, sizeof(packet));
642 }
643 
644 int
ps2GetDeviceID(InputInfoPtr pInfo)645 ps2GetDeviceID(InputInfoPtr pInfo)
646 {
647     unsigned char u;
648     unsigned char packet[] = { 0xf2 };
649 
650     usleep(30000);
651     xf86FlushInput(pInfo->fd);
652     if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
653         return -1;
654     while (1) {
655         if (!readMouse(pInfo,&u))
656             return -1;
657         if (u != 0xFA)
658             break;
659     }
660 #ifdef DEBUG
661     xf86ErrorF("Obtained Mouse Type: %x\n",u);
662 #endif
663     return (int) u;
664 }
665 
666 Bool
ps2Reset(InputInfoPtr pInfo)667 ps2Reset(InputInfoPtr pInfo)
668 {
669     unsigned char u;
670     unsigned char packet[] = { 0xff };
671     unsigned char reply[] = { 0xaa, 0x00 };
672     unsigned int i;
673 #ifdef DEBUG
674    xf86ErrorF("PS/2 Mouse reset\n");
675 #endif
676     if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
677         return FALSE;
678     /* we need a little delay here */
679     xf86WaitForInput(pInfo->fd, 500000);
680     for (i = 0; i < sizeof(reply) ; i++) {
681         if (!readMouse(pInfo,&u)) {
682             goto EXIT;
683         }
684         if (u != reply[i])
685             goto EXIT;
686     }
687     return TRUE;
688 
689  EXIT:
690     xf86FlushInput(pInfo->fd);
691     return FALSE;
692 }
693 
694 static MouseProtocolID
probePs2ProtocolPnP(InputInfoPtr pInfo)695 probePs2ProtocolPnP(InputInfoPtr pInfo)
696 {
697     unsigned char u;
698     MouseProtocolID ret = PROT_UNKNOWN;
699 
700     xf86FlushInput(pInfo->fd);
701 
702     ps2DisableDataReporting(pInfo);
703 
704     if (ps2Reset(pInfo)) { /* Reset PS2 device */
705         unsigned char seq[] = { 243, 200, 243, 100, 243, 80 };
706         /* Try to identify Intelli Mouse */
707         if (ps2SendPacket(pInfo, seq, sizeof(seq))) {
708             u = ps2GetDeviceID(pInfo);
709             if (u == 0x03) {
710                 /* found IntelliMouse now try IntelliExplorer */
711                 unsigned char im_seq[] = { 243, 200, 243, 200, 243, 80 };
712                 if (ps2SendPacket(pInfo,im_seq,sizeof(im_seq))) {
713                     u = ps2GetDeviceID(pInfo);
714                     if (u == 0x04)
715                         ret =  PROT_EXPPS2;
716                     else
717                         ret = PROT_IMPS2;
718                 }
719             } else if (ps2Reset(pInfo))  /* reset again to find sane state */
720                 ret = PROT_PS2;
721         }
722 
723         if (ret != PROT_UNKNOWN)
724             ps2EnableDataReporting(pInfo);
725     }
726     return ret;
727 }
728 
729 static struct ps2protos {
730     int Id;
731     MouseProtocolID protoID;
732 } ps2 [] = {
733     { 0x0, PROT_PS2 },
734     { 0x3, PROT_IMPS2 },
735     { 0x4, PROT_EXPPS2 },
736     { -1 , PROT_UNKNOWN }
737 };
738 
739 
740 static MouseProtocolID
getPs2ProtocolPnP(InputInfoPtr pInfo)741 getPs2ProtocolPnP(InputInfoPtr pInfo)
742 {
743     int Id;
744     int i;
745     MouseProtocolID proto;
746     int count = 4;
747 
748     xf86FlushInput(pInfo->fd);
749 
750     while (--count)
751         if (ps2DisableDataReporting(pInfo))
752             break;
753 
754     if (!count) {
755         proto = PROT_UNKNOWN;
756         goto EXIT;
757     }
758 
759     if ((Id = ps2GetDeviceID(pInfo)) == -1) {
760         proto = PROT_UNKNOWN;
761         goto EXIT;
762     }
763 
764     if (-1 == ps2EnableDataReporting(pInfo)) {
765         proto = PROT_UNKNOWN;
766         goto EXIT;
767     }
768 
769     for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) {
770         if (ps2[i].Id == Id) {
771             xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id);
772             proto =  ps2[i].protoID;
773             goto EXIT;
774         }
775     }
776 
777     proto = PROT_UNKNOWN;
778     xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id);
779 
780  EXIT:
781     xf86FlushInput(pInfo->fd);
782     return proto;
783 }
784 
785