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 */
48typedef 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 */
63typedef struct {
64    const char *name;
65    MouseProtocolID val;
66} symtab_t;
67
68/* PnP EISA/product IDs */
69static 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
134static 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
145static int pnpgets(InputInfoPtr, char *, Bool *prePNP);
146static int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
147static MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf);
148static symtab_t *pnpproto(pnpid_t *);
149static symtab_t *gettoken(symtab_t *, char *, int);
150static MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo);
151static MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo);
152
153static MouseProtocolID
154MouseGetSerialPnpProtocol(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
177MouseProtocolID
178MouseGetPnpProtocol(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 */
233static int
234pnpgets(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     */
367disconnect_idle:
368    i = XF86_M_DTR | XF86_M_RTS;                /* DTR = 1, RTS = 1 */
369    xf86SerialModemSetBits(pInfo->fd, i);
370connect_idle:
371    return 0;
372}
373
374static int
375pnpparse(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 vendor 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 */
500static MouseProtocolID
501prepnpparse(InputInfoPtr pInfo, char *buf)
502{
503    if (buf[0] == 'M' && buf[1] == '3')
504        return PROT_MS;
505    return PROT_UNKNOWN;
506}
507
508
509static symtab_t *
510pnpproto(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
548static symtab_t *
549gettoken(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
562static int
563readMouse(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
573static void
574ps2DisableWrapMode(InputInfoPtr pInfo)
575{
576    unsigned char reset_wrap_mode[] = { 0xEC };
577    ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
578}
579
580Bool
581ps2SendPacket(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("Received: 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 accidentally 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
630static Bool
631ps2DisableDataReporting(InputInfoPtr pInfo)
632{
633    unsigned char packet[] = { 0xF5 };
634    return ps2SendPacket(pInfo, packet, sizeof(packet));
635}
636
637Bool
638ps2EnableDataReporting(InputInfoPtr pInfo)
639{
640    unsigned char packet[] = { 0xF4 };
641    return ps2SendPacket(pInfo, packet, sizeof(packet));
642}
643
644int
645ps2GetDeviceID(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
666Bool
667ps2Reset(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
694static MouseProtocolID
695probePs2ProtocolPnP(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
729static 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
740static MouseProtocolID
741getPs2ProtocolPnP(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