1659607e0Smrg/*
2659607e0Smrg * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3659607e0Smrg *
4659607e0Smrg * Permission to use, copy, modify, distribute, and sell this software and its
5659607e0Smrg * documentation for any purpose is hereby granted without fee, provided that
6659607e0Smrg * the above copyright notice appear in all copies and that both that
7659607e0Smrg * copyright notice and this permission notice appear in supporting
8659607e0Smrg * documentation, and that the name of Kazutaka YOKOTA not be used in
9659607e0Smrg * advertising or publicity pertaining to distribution of the software without
10659607e0Smrg * specific, written prior permission.  Kazutaka YOKOTA makes no representations
11659607e0Smrg * about the suitability of this software for any purpose.  It is provided
12659607e0Smrg * "as is" without express or implied warranty.
13659607e0Smrg *
14659607e0Smrg * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15659607e0Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16659607e0Smrg * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17659607e0Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18659607e0Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19659607e0Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20659607e0Smrg * PERFORMANCE OF THIS SOFTWARE.
21659607e0Smrg */
22659607e0Smrg
23659607e0Smrg#ifdef HAVE_CONFIG_H
24659607e0Smrg#include "config.h"
25659607e0Smrg#endif
26659607e0Smrg
27b73be646Smrg#include <xorg-server.h>
28659607e0Smrg#include <stdio.h>
29659607e0Smrg#include <string.h>
30659607e0Smrg#include <unistd.h>
31659607e0Smrg#include <X11/X.h>
32659607e0Smrg#include <X11/Xproto.h>
33659607e0Smrg#include "inputstr.h"
34659607e0Smrg#include "scrnintstr.h"
35659607e0Smrg#include "xf86.h"
36659607e0Smrg#include "xf86Priv.h"
37659607e0Smrg#include "xf86Xinput.h"
38659607e0Smrg#include "xf86_OSproc.h"
39659607e0Smrg#include "mouse.h"
40659607e0Smrg#include "mousePriv.h"
41659607e0Smrg
42659607e0Smrg#ifdef MOUSEINITDEBUG
43659607e0Smrg# define DEBUG
44659607e0Smrg# define EXTMOUSEDEBUG
45659607e0Smrg#endif
46659607e0Smrg
47659607e0Smrg/* serial PnP ID string */
48659607e0Smrgtypedef struct {
491450c749Smrg    int revision;       /* PnP revision, 100 for 1.00 */
501450c749Smrg    char *eisaid;       /* EISA ID including mfr ID and product ID */
511450c749Smrg    char *serial;       /* serial No, optional */
521450c749Smrg    char *class;        /* device class, optional */
531450c749Smrg    char *compat;       /* list of compatible drivers, optional */
541450c749Smrg    char *description;  /* product description, optional */
551450c749Smrg    int neisaid;        /* length of the above fields... */
56659607e0Smrg    int nserial;
57659607e0Smrg    int nclass;
58659607e0Smrg    int ncompat;
59659607e0Smrg    int ndescription;
60659607e0Smrg} pnpid_t;
61659607e0Smrg
62659607e0Smrg/* symbol table entry */
63659607e0Smrgtypedef struct {
641450c749Smrg    const char *name;
65659607e0Smrg    MouseProtocolID val;
66659607e0Smrg} symtab_t;
67659607e0Smrg
68659607e0Smrg/* PnP EISA/product IDs */
69659607e0Smrgstatic symtab_t pnpprod[] = {
701450c749Smrg    { "KML0001",  PROT_THINKING },      /* Kensington ThinkingMouse */
711450c749Smrg    { "MSH0001",  PROT_IMSERIAL },      /* MS IntelliMouse */
721450c749Smrg    { "MSH0004",  PROT_IMSERIAL },      /* MS IntelliMouse TrackBall */
731450c749Smrg    { "KYEEZ00",  PROT_MS },            /* Genius EZScroll */
741450c749Smrg    { "KYE0001",  PROT_MS },            /* Genius PnP Mouse */
751450c749Smrg    { "KYE0002",  PROT_MS },            /* MouseSystem (Genius?) SmartScroll */
761450c749Smrg    { "KYE0003",  PROT_IMSERIAL },      /* Genius NetMouse */
771450c749Smrg    { "KYE0004",  PROT_IMSERIAL },      /* Genius NetScroll+ (serial) */
781450c749Smrg    { "LGI800C",  PROT_IMSERIAL },      /* Logitech MouseMan (4 button model) */
791450c749Smrg    { "LGI8033",  PROT_IMSERIAL },      /* Logitech Cordless MouseMan Wheel */
801450c749Smrg    { "LGI8050",  PROT_IMSERIAL },      /* Logitech MouseMan+ */
811450c749Smrg    { "LGI8051",  PROT_IMSERIAL },      /* Logitech FirstMouse+ */
821450c749Smrg    { "LGI8001",  PROT_LOGIMAN },       /* Logitech serial */
831450c749Smrg    { "A4W0005",  PROT_IMSERIAL },      /* A4 Tech 4D/4D+ Mouse */
841450c749Smrg    { "PEC9802",  PROT_IMSERIAL },      /* 8D Scroll Mouse */
851450c749Smrg
861450c749Smrg    { "PNP0F00",  PROT_BM },            /* MS bus */
871450c749Smrg    { "PNP0F01",  PROT_MS },            /* MS serial */
881450c749Smrg    { "PNP0F02",  PROT_BM },            /* MS InPort */
891450c749Smrg    { "PNP0F03",  PROT_PS2 },           /* MS PS/2 */
90659607e0Smrg    /*
91659607e0Smrg     * EzScroll returns PNP0F04 in the compatible device field; but it
92659607e0Smrg     * doesn't look compatible... XXX
93659607e0Smrg     */
941450c749Smrg    { "PNP0F04",  PROT_MSC },           /* MouseSystems */
951450c749Smrg    { "PNP0F05",  PROT_MSC },           /* MouseSystems */
96659607e0Smrg#ifdef notyet
971450c749Smrg    { "PNP0F06",  PROT_??? },           /* Genius Mouse */
981450c749Smrg    { "PNP0F07",  PROT_??? },           /* Genius Mouse */
99659607e0Smrg#endif
1001450c749Smrg    { "PNP0F08",  PROT_LOGIMAN },       /* Logitech serial */
1011450c749Smrg    { "PNP0F09",  PROT_MS },            /* MS BallPoint serial */
1021450c749Smrg    { "PNP0F0A",  PROT_MS },            /* MS PnP serial */
1031450c749Smrg    { "PNP0F0B",  PROT_MS },            /* MS PnP BallPoint serial */
1041450c749Smrg    { "PNP0F0C",  PROT_MS },            /* MS serial compatible */
1051450c749Smrg    { "PNP0F0D",  PROT_BM },            /* MS InPort compatible */
1061450c749Smrg    { "PNP0F0E",  PROT_PS2 },           /* MS PS/2 compatible */
1071450c749Smrg    { "PNP0F0F",  PROT_MS },            /* MS BallPoint compatible */
108659607e0Smrg#ifdef notyet
1091450c749Smrg    { "PNP0F10",  PROT_??? },           /* TI QuickPort */
110659607e0Smrg#endif
1111450c749Smrg    { "PNP0F11",  PROT_BM },            /* MS bus compatible */
1121450c749Smrg    { "PNP0F12",  PROT_PS2 },           /* Logitech PS/2 */
1131450c749Smrg    { "PNP0F13",  PROT_PS2 },           /* PS/2 */
114659607e0Smrg#ifdef notyet
1151450c749Smrg    { "PNP0F14",  PROT_??? },           /* MS Kids Mouse */
116659607e0Smrg#endif
1171450c749Smrg    { "PNP0F15",  PROT_BM },            /* Logitech bus */
118659607e0Smrg#ifdef notyet
1191450c749Smrg    { "PNP0F16",  PROT_??? },           /* Logitech SWIFT */
120659607e0Smrg#endif
1211450c749Smrg    { "PNP0F17",  PROT_LOGIMAN },       /* Logitech serial compat */
1221450c749Smrg    { "PNP0F18",  PROT_BM },            /* Logitech bus compatible */
1231450c749Smrg    { "PNP0F19",  PROT_PS2 },           /* Logitech PS/2 compatible */
124659607e0Smrg#ifdef notyet
1251450c749Smrg    { "PNP0F1A",  PROT_??? },           /* Logitech SWIFT compatible */
1261450c749Smrg    { "PNP0F1B",  PROT_??? },           /* HP Omnibook */
1271450c749Smrg    { "PNP0F1C",  PROT_??? },           /* Compaq LTE TrackBall PS/2 */
1281450c749Smrg    { "PNP0F1D",  PROT_??? },           /* Compaq LTE TrackBall serial */
1291450c749Smrg    { "PNP0F1E",  PROT_??? },           /* MS Kids Trackball */
130659607e0Smrg#endif
1311450c749Smrg    { NULL,       PROT_UNKNOWN },
132659607e0Smrg};
133659607e0Smrg
134659607e0Smrgstatic const char *pnpSerial[] = {
1351450c749Smrg        "BaudRate",     "1200",
1361450c749Smrg        "DataBits",     "7",
1371450c749Smrg        "StopBits",     "1",
1381450c749Smrg        "Parity",       "None",
1391450c749Smrg        "FlowControl",  "None",
1401450c749Smrg        "VTime",        "0",
1411450c749Smrg        "VMin",         "1",
1421450c749Smrg        NULL
143659607e0Smrg};
144659607e0Smrg
145659607e0Smrgstatic int pnpgets(InputInfoPtr, char *, Bool *prePNP);
146659607e0Smrgstatic int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
147659607e0Smrgstatic MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf);
148659607e0Smrgstatic symtab_t *pnpproto(pnpid_t *);
149659607e0Smrgstatic symtab_t *gettoken(symtab_t *, char *, int);
150659607e0Smrgstatic MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo);
151659607e0Smrgstatic MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo);
152659607e0Smrg
153659607e0Smrgstatic MouseProtocolID
154659607e0SmrgMouseGetSerialPnpProtocol(InputInfoPtr pInfo)
155659607e0Smrg{
1561450c749Smrg    char buf[256];      /* PnP ID string may be up to 256 bytes long */
157659607e0Smrg    pnpid_t pnpid;
158659607e0Smrg    symtab_t *t;
159659607e0Smrg    int len;
160659607e0Smrg    Bool prePNP;
161659607e0Smrg
1621450c749Smrg    if ((len = pnpgets(pInfo, buf, &prePNP)) > 0)
163659607e0Smrg    {
1641450c749Smrg        if (!prePNP) {
1651450c749Smrg            if (pnpparse(pInfo, &pnpid, buf, len) &&
1661450c749Smrg                (t = pnpproto(&pnpid)) != NULL) {
1671450c749Smrg                xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n",
1681450c749Smrg                            pInfo->name, t->val);
1691450c749Smrg                return (t->val);
1701450c749Smrg            }
1711450c749Smrg        } else
1721450c749Smrg            return prepnpparse(pInfo,buf);
173659607e0Smrg    }
174659607e0Smrg    return PROT_UNKNOWN;
175659607e0Smrg}
176659607e0Smrg
177659607e0SmrgMouseProtocolID
178659607e0SmrgMouseGetPnpProtocol(InputInfoPtr pInfo)
179659607e0Smrg{
180659607e0Smrg    MouseDevPtr  pMse = pInfo->private;
181659607e0Smrg    mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv;
182659607e0Smrg    MouseProtocolID val;
183659607e0Smrg    CARD32 last;
1841450c749Smrg
185659607e0Smrg    if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) {
1861450c749Smrg        if (val == MouseGetSerialPnpProtocol(pInfo))
1871450c749Smrg            return val;
188659607e0Smrg    }
189659607e0Smrg
190659607e0Smrg    last = mPriv->pnpLast;
191659607e0Smrg    mPriv->pnpLast = currentTime.milliseconds;
192659607e0Smrg
193659607e0Smrg    if (last) {
1941450c749Smrg        if (last - currentTime.milliseconds < 100
1951450c749Smrg            || (mPriv->disablePnPauto
1961450c749Smrg                && (last - currentTime.milliseconds < 10000))) {
197659607e0Smrg#ifdef EXTMOUSEDEBUG
1981450c749Smrg            xf86ErrorF("Mouse: Disabling PnP\n");
199659607e0Smrg#endif
2001450c749Smrg            mPriv->disablePnPauto = TRUE;
2011450c749Smrg            return PROT_UNKNOWN;
2021450c749Smrg        }
203659607e0Smrg    }
2041450c749Smrg
205659607e0Smrg#ifdef EXTMOUSEDEBUG
206659607e0Smrg    if (mPriv->disablePnPauto)
2071450c749Smrg        xf86ErrorF("Mouse: Enabling PnP\n");
208659607e0Smrg#endif
209659607e0Smrg    mPriv->disablePnPauto = FALSE;
210659607e0Smrg
211659607e0Smrg    if (mPriv->soft)
2121450c749Smrg        return getPs2ProtocolPnP(pInfo);
213659607e0Smrg    else
2141450c749Smrg        return probePs2ProtocolPnP(pInfo);
215659607e0Smrg}
216659607e0Smrg
217659607e0Smrg/*
2181450c749Smrg * Try to elicit a PnP ID as described in
2191450c749Smrg * Microsoft, Hayes: "Plug and Play External COM Device Specification,
220659607e0Smrg * rev 1.00", 1995.
221659607e0Smrg *
222659607e0Smrg * The routine does not fully implement the COM Enumerator as per Section
223659607e0Smrg * 2.1 of the document.  In particular, we don't have idle state in which
2241450c749Smrg * the driver software monitors the com port for dynamic connection or
2251450c749Smrg * removal of a device at the port, because `moused' simply quits if no
226659607e0Smrg * device is found.
227659607e0Smrg *
2281450c749Smrg * In addition, as PnP COM device enumeration procedure slightly has
229659607e0Smrg * changed since its first publication, devices which follow earlier
2301450c749Smrg * revisions of the above spec. may fail to respond if the rev 1.0
231659607e0Smrg * procedure is used. XXX
232659607e0Smrg */
233659607e0Smrgstatic int
234659607e0Smrgpnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP)
235659607e0Smrg{
236659607e0Smrg    int i;
237659607e0Smrg    char c;
238659607e0Smrg    pointer pnpOpts;
2391450c749Smrg
240659607e0Smrg#if 0
2411450c749Smrg    /*
242659607e0Smrg     * This is the procedure described in rev 1.0 of PnP COM device spec.
2436aab59a7Smrg     * Unfortunately, some devices which conform to earlier revisions of
244659607e0Smrg     * the spec gets confused and do not return the ID string...
245659607e0Smrg     */
246659607e0Smrg
247659607e0Smrg    /* port initialization (2.1.2) */
248659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
2491450c749Smrg        return 0;
2501450c749Smrg    i |= XF86_M_DTR;            /* DTR = 1 */
2511450c749Smrg    i &= ~XF86_M_RTS;           /* RTS = 0 */
252659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
2531450c749Smrg        goto disconnect_idle;
254659607e0Smrg    usleep(200000);
255659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
2561450c749Smrg        (i & XF86_M_DSR) == 0)
2571450c749Smrg        goto disconnect_idle;
258659607e0Smrg
259659607e0Smrg    /* port setup, 1st phase (2.1.3) */
260659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
261659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
2621450c749Smrg    i = TIOCM_DTR | TIOCM_RTS;  /* DTR = 0, RTS = 0 */
263659607e0Smrg    xf86SerialModemClearBits(pInfo->fd, i);
264659607e0Smrg    usleep(200000);
2651450c749Smrg    i = TIOCM_DTR;              /* DTR = 1, RTS = 0 */
266659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
267659607e0Smrg    usleep(200000);
268659607e0Smrg
269659607e0Smrg    /* wait for response, 1st phase (2.1.4) */
270659607e0Smrg    xf86FlushInput(pInfo->fd);
2711450c749Smrg    i = TIOCM_RTS;              /* DTR = 1, RTS = 1 */
272659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
273659607e0Smrg
274659607e0Smrg    /* try to read something */
275659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0) {
276659607e0Smrg
2771450c749Smrg        /* port setup, 2nd phase (2.1.5) */
2781450c749Smrg        i = TIOCM_DTR | TIOCM_RTS;      /* DTR = 0, RTS = 0 */
2791450c749Smrg        xf86SerialModemClearBits(pInfo->fd, i);
280659607e0Smrg        usleep(200000);
281659607e0Smrg
2821450c749Smrg        /* wait for response, 2nd phase (2.1.6) */
2831450c749Smrg        xf86FlushInput(pInfo->fd);
2841450c749Smrg        i = TIOCM_DTR | TIOCM_RTS;      /* DTR = 1, RTS = 1 */
2851450c749Smrg        xf86SerialModemSetBits(pInfo->fd, i);
286659607e0Smrg
287659607e0Smrg        /* try to read something */
2881450c749Smrg        if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
2891450c749Smrg            goto connect_idle;
290659607e0Smrg    }
291659607e0Smrg#else
292659607e0Smrg    /*
293659607e0Smrg     * This is a simplified procedure; it simply toggles RTS.
294659607e0Smrg     */
295659607e0Smrg
296659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
2971450c749Smrg        return 0;
2981450c749Smrg    i |= XF86_M_DTR;            /* DTR = 1 */
2991450c749Smrg    i &= ~XF86_M_RTS;           /* RTS = 0 */
300659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
3011450c749Smrg        goto disconnect_idle;
302659607e0Smrg    usleep(200000);
303659607e0Smrg
304659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
305659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
306659607e0Smrg
3076aab59a7Smrg    /* wait for response */
308659607e0Smrg    xf86FlushInput(pInfo->fd);
3091450c749Smrg    i = XF86_M_DTR | XF86_M_RTS;        /* DTR = 1, RTS = 1 */
310659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
311659607e0Smrg
312659607e0Smrg    /* try to read something */
313659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
314659607e0Smrg        goto connect_idle;
315659607e0Smrg#endif
316659607e0Smrg
317659607e0Smrg    /* collect PnP COM device ID (2.1.7) */
318659607e0Smrg    i = 0;
319659607e0Smrg    *prePNP = FALSE;
3201450c749Smrg
3211450c749Smrg    usleep(200000);     /* the mouse must send `Begin ID' within 200msec */
322659607e0Smrg    while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
3231450c749Smrg        /* we may see "M", or "M3..." before `Begin ID' */
3241450c749Smrg        if (c == 'M')
3251450c749Smrg            *prePNP = TRUE;
3261450c749Smrg
3271450c749Smrg        if ((c == 0x08) || (c == 0x28)) {       /* Begin ID */
3281450c749Smrg            *prePNP = FALSE;
3291450c749Smrg            buf[0] = c;
3301450c749Smrg            i = 1;
3311450c749Smrg            break;
332659607e0Smrg        }
3331450c749Smrg        if (*prePNP)
3341450c749Smrg            buf[i++] = c;
3351450c749Smrg
3361450c749Smrg        if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
3371450c749Smrg            break;
338659607e0Smrg    }
339659607e0Smrg    if (i <= 0) {
3401450c749Smrg        /* we haven't seen `Begin ID' in time... */
3411450c749Smrg        goto connect_idle;
342659607e0Smrg    }
343659607e0Smrg    if (*prePNP)
3441450c749Smrg        return i;
3451450c749Smrg
3461450c749Smrg    ++c;                        /* make it `End ID' */
347659607e0Smrg    for (;;) {
3481450c749Smrg        if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
3491450c749Smrg            break;
3501450c749Smrg
3511450c749Smrg        xf86ReadSerial(pInfo->fd, &buf[i], 1);
3521450c749Smrg        if (buf[i++] == c)      /* End ID */
3531450c749Smrg            break;
3541450c749Smrg        if (i >= 256)
3551450c749Smrg            break;
356659607e0Smrg    }
357659607e0Smrg    if (buf[i - 1] != c)
3581450c749Smrg        goto connect_idle;
359659607e0Smrg    return i;
360659607e0Smrg
361659607e0Smrg    /*
3621450c749Smrg     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
3631450c749Smrg     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
3641450c749Smrg     * assuming there is something at the port even if it didn't
365659607e0Smrg     * respond to the PnP enumeration procedure.
366659607e0Smrg     */
367659607e0Smrgdisconnect_idle:
3681450c749Smrg    i = XF86_M_DTR | XF86_M_RTS;                /* DTR = 1, RTS = 1 */
369659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
370659607e0Smrgconnect_idle:
371659607e0Smrg    return 0;
372659607e0Smrg}
373659607e0Smrg
374659607e0Smrgstatic int
375659607e0Smrgpnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len)
376659607e0Smrg{
377659607e0Smrg    char s[3];
378659607e0Smrg    int offset;
379659607e0Smrg    int sum = 0;
380659607e0Smrg    int i, j;
381659607e0Smrg
382659607e0Smrg    id->revision = 0;
383659607e0Smrg    id->eisaid = NULL;
384659607e0Smrg    id->serial = NULL;
385659607e0Smrg    id->class = NULL;
386659607e0Smrg    id->compat = NULL;
387659607e0Smrg    id->description = NULL;
388659607e0Smrg    id->neisaid = 0;
389659607e0Smrg    id->nserial = 0;
390659607e0Smrg    id->nclass = 0;
391659607e0Smrg    id->ncompat = 0;
392659607e0Smrg    id->ndescription = 0;
393659607e0Smrg
394659607e0Smrg    offset = 0x28 - buf[0];
395659607e0Smrg
396659607e0Smrg    /* calculate checksum */
397659607e0Smrg    for (i = 0; i < len - 3; ++i) {
3981450c749Smrg        sum += buf[i];
3991450c749Smrg        buf[i] += offset;
400659607e0Smrg    }
401659607e0Smrg    sum += buf[len - 1];
402659607e0Smrg    for (; i < len; ++i)
4031450c749Smrg        buf[i] += offset;
404659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
4051450c749Smrg                len, len, buf);
406659607e0Smrg
407659607e0Smrg    /* revision */
408659607e0Smrg    buf[1] -= offset;
409659607e0Smrg    buf[2] -= offset;
410659607e0Smrg    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
411659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name,
4121450c749Smrg                id->revision / 100, id->revision % 100);
413659607e0Smrg
4144d6d3f96Smrg    /* EISA vendor and product ID */
415659607e0Smrg    id->eisaid = &buf[3];
416659607e0Smrg    id->neisaid = 7;
417659607e0Smrg
418659607e0Smrg    /* option strings */
419659607e0Smrg    i = 10;
420659607e0Smrg    if (buf[i] == '\\') {
421659607e0Smrg        /* device serial # */
422659607e0Smrg        for (j = ++i; i < len; ++i) {
423659607e0Smrg            if (buf[i] == '\\')
4241450c749Smrg                break;
425659607e0Smrg        }
4261450c749Smrg        if (i >= len)
4271450c749Smrg            i -= 3;
4281450c749Smrg        if (i - j == 8) {
429659607e0Smrg            id->serial = &buf[j];
430659607e0Smrg            id->nserial = 8;
4311450c749Smrg        }
432659607e0Smrg    }
433659607e0Smrg    if (buf[i] == '\\') {
434659607e0Smrg        /* PnP class */
435659607e0Smrg        for (j = ++i; i < len; ++i) {
436659607e0Smrg            if (buf[i] == '\\')
4371450c749Smrg                break;
438659607e0Smrg        }
4391450c749Smrg        if (i >= len)
4401450c749Smrg            i -= 3;
4411450c749Smrg        if (i > j + 1) {
442659607e0Smrg            id->class = &buf[j];
443659607e0Smrg            id->nclass = i - j;
444659607e0Smrg        }
445659607e0Smrg    }
446659607e0Smrg    if (buf[i] == '\\') {
4471450c749Smrg        /* compatible driver */
448659607e0Smrg        for (j = ++i; i < len; ++i) {
449659607e0Smrg            if (buf[i] == '\\')
4501450c749Smrg                break;
451659607e0Smrg        }
4521450c749Smrg        /*
4531450c749Smrg         * PnP COM spec prior to v0.96 allowed '*' in this field,
4541450c749Smrg         * it's not allowed now; just ignore it.
4551450c749Smrg         */
4561450c749Smrg        if (buf[j] == '*')
4571450c749Smrg            ++j;
4581450c749Smrg        if (i >= len)
4591450c749Smrg            i -= 3;
4601450c749Smrg        if (i > j + 1) {
461659607e0Smrg            id->compat = &buf[j];
462659607e0Smrg            id->ncompat = i - j;
463659607e0Smrg        }
464659607e0Smrg    }
465659607e0Smrg    if (buf[i] == '\\') {
4661450c749Smrg        /* product description */
467659607e0Smrg        for (j = ++i; i < len; ++i) {
468659607e0Smrg            if (buf[i] == ';')
4691450c749Smrg                break;
470659607e0Smrg        }
4711450c749Smrg        if (i >= len)
4721450c749Smrg            i -= 3;
4731450c749Smrg        if (i > j + 1) {
474659607e0Smrg            id->description = &buf[j];
475659607e0Smrg            id->ndescription = i - j;
476659607e0Smrg        }
477659607e0Smrg    }
478659607e0Smrg
479659607e0Smrg    /* checksum exists if there are any optional fields */
480659607e0Smrg    if ((id->nserial > 0) || (id->nclass > 0)
4811450c749Smrg        || (id->ncompat > 0) || (id->ndescription > 0)) {
4821450c749Smrg        xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum);
483659607e0Smrg        sprintf(s, "%02X", sum & 0x0ff);
484659607e0Smrg        if (strncmp(s, &buf[len - 3], 2) != 0) {
485659607e0Smrg#if 0
4861450c749Smrg            /*
4871450c749Smrg             * Checksum error!!
4881450c749Smrg             * I found some mice do not comply with the PnP COM device
4891450c749Smrg             * spec regarding checksum... XXX
4901450c749Smrg             */
4911450c749Smrg            return FALSE;
492659607e0Smrg#endif
493659607e0Smrg        }
494659607e0Smrg    }
495659607e0Smrg
496659607e0Smrg    return TRUE;
497659607e0Smrg}
498659607e0Smrg
499659607e0Smrg/* We can only identify MS at the moment */
500659607e0Smrgstatic MouseProtocolID
501659607e0Smrgprepnpparse(InputInfoPtr pInfo, char *buf)
502659607e0Smrg{
503659607e0Smrg    if (buf[0] == 'M' && buf[1] == '3')
5041450c749Smrg        return PROT_MS;
505659607e0Smrg    return PROT_UNKNOWN;
506659607e0Smrg}
507659607e0Smrg
508659607e0Smrg
509659607e0Smrgstatic symtab_t *
510659607e0Smrgpnpproto(pnpid_t *id)
511659607e0Smrg{
512659607e0Smrg    symtab_t *t;
513659607e0Smrg    int i, j;
514659607e0Smrg
515659607e0Smrg    if (id->nclass > 0)
5161450c749Smrg        if (strncmp(id->class, "MOUSE", id->nclass) != 0)
5171450c749Smrg            /* this is not a mouse! */
5181450c749Smrg            return NULL;
519659607e0Smrg
520659607e0Smrg    if (id->neisaid > 0) {
521659607e0Smrg        t = gettoken(pnpprod, id->eisaid, id->neisaid);
5221450c749Smrg        if (t->val != -1)
523659607e0Smrg            return t;
524659607e0Smrg    }
525659607e0Smrg
526659607e0Smrg    /*
527659607e0Smrg     * The 'Compatible drivers' field may contain more than one
528659607e0Smrg     * ID separated by ','.
529659607e0Smrg     */
530659607e0Smrg    if (id->ncompat <= 0)
5311450c749Smrg        return NULL;
532659607e0Smrg    for (i = 0; i < id->ncompat; ++i) {
533659607e0Smrg        for (j = i; id->compat[i] != ','; ++i)
534659607e0Smrg            if (i >= id->ncompat)
5351450c749Smrg                break;
536659607e0Smrg        if (i > j) {
537659607e0Smrg            t = gettoken(pnpprod, id->compat + j, i - j);
5381450c749Smrg            if (t->val != -1)
539659607e0Smrg                return t;
5401450c749Smrg        }
541659607e0Smrg    }
542659607e0Smrg
543659607e0Smrg    return NULL;
544659607e0Smrg}
545659607e0Smrg
546659607e0Smrg/* name/val mapping */
547659607e0Smrg
548659607e0Smrgstatic symtab_t *
5496aab59a7Smrggettoken(symtab_t *tab, char *s, int len)
550659607e0Smrg{
551659607e0Smrg    int i;
552659607e0Smrg
553659607e0Smrg    for (i = 0; tab[i].name != NULL; ++i) {
5541450c749Smrg        if (strncmp(tab[i].name, s, len) == 0)
5551450c749Smrg            break;
556659607e0Smrg    }
557659607e0Smrg    return &tab[i];
558659607e0Smrg}
559659607e0Smrg
560659607e0Smrg/******************* PS/2 PnP probing ****************/
561659607e0Smrg
562659607e0Smrgstatic int
563659607e0SmrgreadMouse(InputInfoPtr pInfo, unsigned char *u)
564659607e0Smrg{
565659607e0Smrg
566659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
5671450c749Smrg        return FALSE;
568659607e0Smrg
569659607e0Smrg    xf86ReadSerial(pInfo->fd, u, 1);
570659607e0Smrg    return TRUE;
571659607e0Smrg}
572659607e0Smrg
573659607e0Smrgstatic void
574659607e0Smrgps2DisableWrapMode(InputInfoPtr pInfo)
575659607e0Smrg{
576659607e0Smrg    unsigned char reset_wrap_mode[] = { 0xEC };
577659607e0Smrg    ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
578659607e0Smrg}
579659607e0Smrg
580659607e0SmrgBool
581659607e0Smrgps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len)
582659607e0Smrg{
583659607e0Smrg    unsigned char c;
584659607e0Smrg    int i,j;
5851450c749Smrg
586659607e0Smrg#ifdef DEBUG
587659607e0Smrg    xf86ErrorF("Ps/2 data package:");
588659607e0Smrg    for (i = 0; i < len; i++)
5891450c749Smrg        xf86ErrorF(" %x", *(bytes + i));
590659607e0Smrg    xf86ErrorF("\n");
591659607e0Smrg#endif
592659607e0Smrg
593659607e0Smrg    for (i = 0; i < len; i++) {
5941450c749Smrg        for (j = 0; j < 10; j++) {
5951450c749Smrg            xf86WriteSerial(pInfo->fd, bytes + i, 1);
5961450c749Smrg            usleep(10000);
5971450c749Smrg            if (!readMouse(pInfo,&c)) {
598659607e0Smrg#ifdef DEBUG
5991450c749Smrg                xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i));
600659607e0Smrg#endif
6011450c749Smrg                return FALSE;
6021450c749Smrg            }
603659607e0Smrg#ifdef DEBUG
6044d6d3f96Smrg            xf86ErrorF("Received: 0x%x\n",c);
605659607e0Smrg#endif
6061450c749Smrg            if (c == 0xFA) /* ACK */
6071450c749Smrg                break;
6081450c749Smrg
6091450c749Smrg            if (c == 0xFE) /* resend */
6101450c749Smrg                continue;
611659607e0Smrg
612659607e0Smrg
6131450c749Smrg            if (c == 0xFC) /* error */
6141450c749Smrg                return FALSE;
615659607e0Smrg
6164d6d3f96Smrg            /* Some mice accidentally enter wrap mode during init */
6171450c749Smrg            if (c == *(bytes + i)    /* wrap mode */
6181450c749Smrg                && (*(bytes + i) != 0xEC)) /* avoid recursion */
6191450c749Smrg                ps2DisableWrapMode(pInfo);
620659607e0Smrg
6211450c749Smrg            return FALSE;
6221450c749Smrg        }
6231450c749Smrg        if (j == 10)
6241450c749Smrg            return FALSE;
625659607e0Smrg    }
6261450c749Smrg
627659607e0Smrg    return TRUE;
628659607e0Smrg}
6291450c749Smrg
630659607e0Smrgstatic Bool
631659607e0Smrgps2DisableDataReporting(InputInfoPtr pInfo)
632659607e0Smrg{
633659607e0Smrg    unsigned char packet[] = { 0xF5 };
634659607e0Smrg    return ps2SendPacket(pInfo, packet, sizeof(packet));
635659607e0Smrg}
636659607e0Smrg
637659607e0SmrgBool
638659607e0Smrgps2EnableDataReporting(InputInfoPtr pInfo)
639659607e0Smrg{
640659607e0Smrg    unsigned char packet[] = { 0xF4 };
641659607e0Smrg    return ps2SendPacket(pInfo, packet, sizeof(packet));
642659607e0Smrg}
643659607e0Smrg
644659607e0Smrgint
645659607e0Smrgps2GetDeviceID(InputInfoPtr pInfo)
646659607e0Smrg{
647659607e0Smrg    unsigned char u;
648659607e0Smrg    unsigned char packet[] = { 0xf2 };
649659607e0Smrg
650659607e0Smrg    usleep(30000);
651659607e0Smrg    xf86FlushInput(pInfo->fd);
6521450c749Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
6531450c749Smrg        return -1;
654659607e0Smrg    while (1) {
6551450c749Smrg        if (!readMouse(pInfo,&u))
6561450c749Smrg            return -1;
6571450c749Smrg        if (u != 0xFA)
6581450c749Smrg            break;
659659607e0Smrg    }
660659607e0Smrg#ifdef DEBUG
661659607e0Smrg    xf86ErrorF("Obtained Mouse Type: %x\n",u);
662659607e0Smrg#endif
663659607e0Smrg    return (int) u;
664659607e0Smrg}
665659607e0Smrg
666659607e0SmrgBool
667659607e0Smrgps2Reset(InputInfoPtr pInfo)
668659607e0Smrg{
669659607e0Smrg    unsigned char u;
670659607e0Smrg    unsigned char packet[] = { 0xff };
671659607e0Smrg    unsigned char reply[] = { 0xaa, 0x00 };
672659607e0Smrg    unsigned int i;
673659607e0Smrg#ifdef DEBUG
674659607e0Smrg   xf86ErrorF("PS/2 Mouse reset\n");
675659607e0Smrg#endif
6761450c749Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
6771450c749Smrg        return FALSE;
678659607e0Smrg    /* we need a little delay here */
679659607e0Smrg    xf86WaitForInput(pInfo->fd, 500000);
680659607e0Smrg    for (i = 0; i < sizeof(reply) ; i++) {
6811450c749Smrg        if (!readMouse(pInfo,&u)) {
6821450c749Smrg            goto EXIT;
6831450c749Smrg        }
6841450c749Smrg        if (u != reply[i])
6851450c749Smrg            goto EXIT;
686659607e0Smrg    }
687659607e0Smrg    return TRUE;
6881450c749Smrg
689659607e0Smrg EXIT:
690659607e0Smrg    xf86FlushInput(pInfo->fd);
691659607e0Smrg    return FALSE;
692659607e0Smrg}
693659607e0Smrg
694659607e0Smrgstatic MouseProtocolID
695659607e0SmrgprobePs2ProtocolPnP(InputInfoPtr pInfo)
696659607e0Smrg{
697659607e0Smrg    unsigned char u;
698659607e0Smrg    MouseProtocolID ret = PROT_UNKNOWN;
699659607e0Smrg
700659607e0Smrg    xf86FlushInput(pInfo->fd);
701659607e0Smrg
702659607e0Smrg    ps2DisableDataReporting(pInfo);
7031450c749Smrg
704659607e0Smrg    if (ps2Reset(pInfo)) { /* Reset PS2 device */
7051450c749Smrg        unsigned char seq[] = { 243, 200, 243, 100, 243, 80 };
7061450c749Smrg        /* Try to identify Intelli Mouse */
7071450c749Smrg        if (ps2SendPacket(pInfo, seq, sizeof(seq))) {
7081450c749Smrg            u = ps2GetDeviceID(pInfo);
7091450c749Smrg            if (u == 0x03) {
7101450c749Smrg                /* found IntelliMouse now try IntelliExplorer */
7111450c749Smrg                unsigned char im_seq[] = { 243, 200, 243, 200, 243, 80 };
7121450c749Smrg                if (ps2SendPacket(pInfo,im_seq,sizeof(im_seq))) {
7131450c749Smrg                    u = ps2GetDeviceID(pInfo);
7141450c749Smrg                    if (u == 0x04)
7151450c749Smrg                        ret =  PROT_EXPPS2;
7161450c749Smrg                    else
7171450c749Smrg                        ret = PROT_IMPS2;
7181450c749Smrg                }
7191450c749Smrg            } else if (ps2Reset(pInfo))  /* reset again to find sane state */
7201450c749Smrg                ret = PROT_PS2;
7211450c749Smrg        }
7221450c749Smrg
7231450c749Smrg        if (ret != PROT_UNKNOWN)
7241450c749Smrg            ps2EnableDataReporting(pInfo);
725659607e0Smrg    }
726659607e0Smrg    return ret;
727659607e0Smrg}
728659607e0Smrg
729659607e0Smrgstatic struct ps2protos {
7301450c749Smrg    int Id;
731659607e0Smrg    MouseProtocolID protoID;
732659607e0Smrg} ps2 [] = {
733659607e0Smrg    { 0x0, PROT_PS2 },
734659607e0Smrg    { 0x3, PROT_IMPS2 },
735659607e0Smrg    { 0x4, PROT_EXPPS2 },
736659607e0Smrg    { -1 , PROT_UNKNOWN }
737659607e0Smrg};
738659607e0Smrg
739659607e0Smrg
740659607e0Smrgstatic MouseProtocolID
741659607e0SmrggetPs2ProtocolPnP(InputInfoPtr pInfo)
742659607e0Smrg{
743659607e0Smrg    int Id;
744659607e0Smrg    int i;
745659607e0Smrg    MouseProtocolID proto;
746659607e0Smrg    int count = 4;
747659607e0Smrg
748659607e0Smrg    xf86FlushInput(pInfo->fd);
749659607e0Smrg
750659607e0Smrg    while (--count)
7511450c749Smrg        if (ps2DisableDataReporting(pInfo))
7521450c749Smrg            break;
7531450c749Smrg
754659607e0Smrg    if (!count) {
7551450c749Smrg        proto = PROT_UNKNOWN;
7561450c749Smrg        goto EXIT;
757659607e0Smrg    }
7581450c749Smrg
759659607e0Smrg    if ((Id = ps2GetDeviceID(pInfo)) == -1) {
7601450c749Smrg        proto = PROT_UNKNOWN;
7611450c749Smrg        goto EXIT;
762659607e0Smrg    }
763659607e0Smrg
764659607e0Smrg    if (-1 == ps2EnableDataReporting(pInfo)) {
7651450c749Smrg        proto = PROT_UNKNOWN;
7661450c749Smrg        goto EXIT;
767659607e0Smrg    }
768659607e0Smrg
769659607e0Smrg    for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) {
7701450c749Smrg        if (ps2[i].Id == Id) {
7711450c749Smrg            xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id);
7721450c749Smrg            proto =  ps2[i].protoID;
7731450c749Smrg            goto EXIT;
7741450c749Smrg        }
775659607e0Smrg    }
7761450c749Smrg
777659607e0Smrg    proto = PROT_UNKNOWN;
778659607e0Smrg    xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id);
7791450c749Smrg
780659607e0Smrg EXIT:
781659607e0Smrg    xf86FlushInput(pInfo->fd);
782659607e0Smrg    return proto;
783659607e0Smrg}
784659607e0Smrg
785