pnp.c revision b73be646
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 "xf86OSmouse.h"
40659607e0Smrg#include "mouse.h"
41659607e0Smrg#include "mousePriv.h"
42659607e0Smrg
43659607e0Smrg#ifdef MOUSEINITDEBUG
44659607e0Smrg# define DEBUG
45659607e0Smrg# define EXTMOUSEDEBUG
46659607e0Smrg#endif
47659607e0Smrg
48659607e0Smrg/* serial PnP ID string */
49659607e0Smrgtypedef struct {
50659607e0Smrg    int revision;	/* PnP revision, 100 for 1.00 */
51659607e0Smrg    char *eisaid;	/* EISA ID including mfr ID and product ID */
52659607e0Smrg    char *serial;	/* serial No, optional */
53659607e0Smrg    char *class;	/* device class, optional */
54659607e0Smrg    char *compat;	/* list of compatible drivers, optional */
55659607e0Smrg    char *description;	/* product description, optional */
56659607e0Smrg    int neisaid;	/* length of the above fields... */
57659607e0Smrg    int nserial;
58659607e0Smrg    int nclass;
59659607e0Smrg    int ncompat;
60659607e0Smrg    int ndescription;
61659607e0Smrg} pnpid_t;
62659607e0Smrg
63659607e0Smrg/* symbol table entry */
64659607e0Smrgtypedef struct {
65659607e0Smrg    char *name;
66659607e0Smrg    MouseProtocolID val;
67659607e0Smrg} symtab_t;
68659607e0Smrg
69659607e0Smrg/* PnP EISA/product IDs */
70659607e0Smrgstatic symtab_t pnpprod[] = {
716aab59a7Smrg    { "KML0001",  PROT_THINKING },	/* Kensington ThinkingMouse */
72659607e0Smrg    { "MSH0001",  PROT_IMSERIAL },	/* MS IntelliMouse */
73659607e0Smrg    { "MSH0004",  PROT_IMSERIAL },	/* MS IntelliMouse TrackBall */
74659607e0Smrg    { "KYEEZ00",  PROT_MS },		/* Genius EZScroll */
75659607e0Smrg    { "KYE0001",  PROT_MS },		/* Genius PnP Mouse */
76659607e0Smrg    { "KYE0002",  PROT_MS },		/* MouseSystem (Genius?) SmartScroll */
77659607e0Smrg    { "KYE0003",  PROT_IMSERIAL },	/* Genius NetMouse */
78b73be646Smrg    { "KYE0004",  PROT_IMSERIAL },	/* Genius NetScroll+ (serial) */
79659607e0Smrg    { "LGI800C",  PROT_IMSERIAL },	/* Logitech MouseMan (4 button model) */
80659607e0Smrg    { "LGI8033",  PROT_IMSERIAL },	/* Logitech Cordless MouseMan Wheel */
81659607e0Smrg    { "LGI8050",  PROT_IMSERIAL },	/* Logitech MouseMan+ */
82659607e0Smrg    { "LGI8051",  PROT_IMSERIAL },	/* Logitech FirstMouse+ */
83659607e0Smrg    { "LGI8001",  PROT_LOGIMAN },	/* Logitech serial */
84659607e0Smrg    { "A4W0005",  PROT_IMSERIAL },	/* A4 Tech 4D/4D+ Mouse */
85659607e0Smrg    { "PEC9802",  PROT_IMSERIAL },	/* 8D Scroll Mouse */
86659607e0Smrg
87659607e0Smrg    { "PNP0F00",  PROT_BM },		/* MS bus */
88659607e0Smrg    { "PNP0F01",  PROT_MS },		/* MS serial */
89659607e0Smrg    { "PNP0F02",  PROT_BM },		/* MS InPort */
90659607e0Smrg    { "PNP0F03",  PROT_PS2 },		/* MS PS/2 */
91659607e0Smrg    /*
92659607e0Smrg     * EzScroll returns PNP0F04 in the compatible device field; but it
93659607e0Smrg     * doesn't look compatible... XXX
94659607e0Smrg     */
95659607e0Smrg    { "PNP0F04",  PROT_MSC },		/* MouseSystems */
96659607e0Smrg    { "PNP0F05",  PROT_MSC },		/* MouseSystems */
97659607e0Smrg#ifdef notyet
98659607e0Smrg    { "PNP0F06",  PROT_??? },		/* Genius Mouse */
99659607e0Smrg    { "PNP0F07",  PROT_??? },		/* Genius Mouse */
100659607e0Smrg#endif
101659607e0Smrg    { "PNP0F08",  PROT_LOGIMAN },	/* Logitech serial */
102659607e0Smrg    { "PNP0F09",  PROT_MS },		/* MS BallPoint serial */
103659607e0Smrg    { "PNP0F0A",  PROT_MS },		/* MS PnP serial */
104659607e0Smrg    { "PNP0F0B",  PROT_MS },		/* MS PnP BallPoint serial */
1056aab59a7Smrg    { "PNP0F0C",  PROT_MS },		/* MS serial compatible */
1066aab59a7Smrg    { "PNP0F0D",  PROT_BM },		/* MS InPort compatible */
1076aab59a7Smrg    { "PNP0F0E",  PROT_PS2 },		/* MS PS/2 compatible */
1086aab59a7Smrg    { "PNP0F0F",  PROT_MS },		/* MS BallPoint compatible */
109659607e0Smrg#ifdef notyet
110659607e0Smrg    { "PNP0F10",  PROT_??? },		/* TI QuickPort */
111659607e0Smrg#endif
1126aab59a7Smrg    { "PNP0F11",  PROT_BM },		/* MS bus compatible */
113659607e0Smrg    { "PNP0F12",  PROT_PS2 },		/* Logitech PS/2 */
114659607e0Smrg    { "PNP0F13",  PROT_PS2 },		/* PS/2 */
115659607e0Smrg#ifdef notyet
116659607e0Smrg    { "PNP0F14",  PROT_??? },		/* MS Kids Mouse */
117659607e0Smrg#endif
118659607e0Smrg    { "PNP0F15",  PROT_BM },		/* Logitech bus */
119659607e0Smrg#ifdef notyet
120659607e0Smrg    { "PNP0F16",  PROT_??? },		/* Logitech SWIFT */
121659607e0Smrg#endif
122659607e0Smrg    { "PNP0F17",  PROT_LOGIMAN },	/* Logitech serial compat */
123659607e0Smrg    { "PNP0F18",  PROT_BM },		/* Logitech bus compatible */
124659607e0Smrg    { "PNP0F19",  PROT_PS2 },		/* Logitech PS/2 compatible */
125659607e0Smrg#ifdef notyet
126659607e0Smrg    { "PNP0F1A",  PROT_??? },		/* Logitech SWIFT compatible */
127659607e0Smrg    { "PNP0F1B",  PROT_??? },		/* HP Omnibook */
128659607e0Smrg    { "PNP0F1C",  PROT_??? },		/* Compaq LTE TrackBall PS/2 */
129659607e0Smrg    { "PNP0F1D",  PROT_??? },		/* Compaq LTE TrackBall serial */
130659607e0Smrg    { "PNP0F1E",  PROT_??? },		/* MS Kids Trackball */
131659607e0Smrg#endif
132659607e0Smrg    { NULL,	  PROT_UNKNOWN },
133659607e0Smrg};
134659607e0Smrg
135659607e0Smrgstatic const char *pnpSerial[] = {
136659607e0Smrg	"BaudRate",	"1200",
137659607e0Smrg	"DataBits",	"7",
138659607e0Smrg	"StopBits",	"1",
139659607e0Smrg	"Parity",	"None",
140659607e0Smrg	"FlowControl",	"None",
141659607e0Smrg	"VTime",	"0",
142659607e0Smrg	"VMin",		"1",
143659607e0Smrg	NULL
144659607e0Smrg};
145659607e0Smrg
146659607e0Smrgstatic int pnpgets(InputInfoPtr, char *, Bool *prePNP);
147659607e0Smrgstatic int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
148659607e0Smrgstatic MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf);
149659607e0Smrgstatic symtab_t *pnpproto(pnpid_t *);
150659607e0Smrgstatic symtab_t *gettoken(symtab_t *, char *, int);
151659607e0Smrgstatic MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo);
152659607e0Smrgstatic MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo);
153659607e0Smrg
154659607e0Smrgstatic MouseProtocolID
155659607e0SmrgMouseGetSerialPnpProtocol(InputInfoPtr pInfo)
156659607e0Smrg{
157659607e0Smrg    char buf[256];	/* PnP ID string may be up to 256 bytes long */
158659607e0Smrg    pnpid_t pnpid;
159659607e0Smrg    symtab_t *t;
160659607e0Smrg    int len;
161659607e0Smrg    Bool prePNP;
162659607e0Smrg
163659607e0Smrg    if ((len = pnpgets(pInfo, buf, &prePNP)) > 0)
164659607e0Smrg    {
165659607e0Smrg	if (!prePNP) {
166659607e0Smrg	    if (pnpparse(pInfo, &pnpid, buf, len) &&
167659607e0Smrg		(t = pnpproto(&pnpid)) != NULL) {
168659607e0Smrg		xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n",
169659607e0Smrg			    pInfo->name, t->val);
170659607e0Smrg		return (t->val);
171659607e0Smrg	    }
172659607e0Smrg	} else
173659607e0Smrg	    return prepnpparse(pInfo,buf);
174659607e0Smrg    }
175659607e0Smrg    return PROT_UNKNOWN;
176659607e0Smrg}
177659607e0Smrg
178659607e0SmrgMouseProtocolID
179659607e0SmrgMouseGetPnpProtocol(InputInfoPtr pInfo)
180659607e0Smrg{
181659607e0Smrg    MouseDevPtr  pMse = pInfo->private;
182659607e0Smrg    mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv;
183659607e0Smrg    MouseProtocolID val;
184659607e0Smrg    CARD32 last;
185659607e0Smrg
186659607e0Smrg    if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) {
187659607e0Smrg	if (val == MouseGetSerialPnpProtocol(pInfo))
188659607e0Smrg	    return val;
189659607e0Smrg    }
190659607e0Smrg
191659607e0Smrg    last = mPriv->pnpLast;
192659607e0Smrg    mPriv->pnpLast = currentTime.milliseconds;
193659607e0Smrg
194659607e0Smrg    if (last) {
195659607e0Smrg	if (last - currentTime.milliseconds < 100
196659607e0Smrg	    || (mPriv->disablePnPauto
197659607e0Smrg		&& (last - currentTime.milliseconds < 10000))) {
198659607e0Smrg#ifdef EXTMOUSEDEBUG
199659607e0Smrg	    xf86ErrorF("Mouse: Disabling PnP\n");
200659607e0Smrg#endif
201659607e0Smrg	    mPriv->disablePnPauto = TRUE;
202659607e0Smrg	    return PROT_UNKNOWN;
203659607e0Smrg	}
204659607e0Smrg    }
205659607e0Smrg
206659607e0Smrg#ifdef EXTMOUSEDEBUG
207659607e0Smrg    if (mPriv->disablePnPauto)
208659607e0Smrg	xf86ErrorF("Mouse: Enabling PnP\n");
209659607e0Smrg#endif
210659607e0Smrg    mPriv->disablePnPauto = FALSE;
211659607e0Smrg
212659607e0Smrg    if (mPriv->soft)
213659607e0Smrg	return getPs2ProtocolPnP(pInfo);
214659607e0Smrg    else
215659607e0Smrg	return probePs2ProtocolPnP(pInfo);
216659607e0Smrg}
217659607e0Smrg
218659607e0Smrg/*
219659607e0Smrg * Try to elicit a PnP ID as described in
220659607e0Smrg * Microsoft, Hayes: "Plug and Play External COM Device Specification,
221659607e0Smrg * rev 1.00", 1995.
222659607e0Smrg *
223659607e0Smrg * The routine does not fully implement the COM Enumerator as per Section
224659607e0Smrg * 2.1 of the document.  In particular, we don't have idle state in which
225659607e0Smrg * the driver software monitors the com port for dynamic connection or
226659607e0Smrg * removal of a device at the port, because `moused' simply quits if no
227659607e0Smrg * device is found.
228659607e0Smrg *
229659607e0Smrg * In addition, as PnP COM device enumeration procedure slightly has
230659607e0Smrg * changed since its first publication, devices which follow earlier
231659607e0Smrg * revisions of the above spec. may fail to respond if the rev 1.0
232659607e0Smrg * procedure is used. XXX
233659607e0Smrg */
234659607e0Smrgstatic int
235659607e0Smrgpnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP)
236659607e0Smrg{
237659607e0Smrg    int i;
238659607e0Smrg    char c;
239659607e0Smrg    pointer pnpOpts;
240659607e0Smrg
241659607e0Smrg#if 0
242659607e0Smrg    /*
243659607e0Smrg     * This is the procedure described in rev 1.0 of PnP COM device spec.
2446aab59a7Smrg     * Unfortunately, some devices which conform to earlier revisions of
245659607e0Smrg     * the spec gets confused and do not return the ID string...
246659607e0Smrg     */
247659607e0Smrg
248659607e0Smrg    /* port initialization (2.1.2) */
249659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
250659607e0Smrg	return 0;
251659607e0Smrg    i |= XF86_M_DTR;		/* DTR = 1 */
252659607e0Smrg    i &= ~XF86_M_RTS;		/* RTS = 0 */
253659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
254659607e0Smrg	goto disconnect_idle;
255659607e0Smrg    usleep(200000);
256659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
257659607e0Smrg	(i & XF86_M_DSR) == 0)
258659607e0Smrg	goto disconnect_idle;
259659607e0Smrg
260659607e0Smrg    /* port setup, 1st phase (2.1.3) */
261659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
262659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
263659607e0Smrg    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
264659607e0Smrg    xf86SerialModemClearBits(pInfo->fd, i);
265659607e0Smrg    usleep(200000);
266659607e0Smrg    i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
267659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
268659607e0Smrg    usleep(200000);
269659607e0Smrg
270659607e0Smrg    /* wait for response, 1st phase (2.1.4) */
271659607e0Smrg    xf86FlushInput(pInfo->fd);
272659607e0Smrg    i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
273659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
274659607e0Smrg
275659607e0Smrg    /* try to read something */
276659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0) {
277659607e0Smrg
278659607e0Smrg	/* port setup, 2nd phase (2.1.5) */
279659607e0Smrg        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
280659607e0Smrg	xf86SerialModemClearBits(pInfo->fd, i);
281659607e0Smrg        usleep(200000);
282659607e0Smrg
2836aab59a7Smrg	/* wait for response, 2nd phase (2.1.6) */
284659607e0Smrg	xf86FlushInput(pInfo->fd);
285659607e0Smrg        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
286659607e0Smrg	xf86SerialModemSetBits(pInfo->fd, i);
287659607e0Smrg
288659607e0Smrg        /* try to read something */
289659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
290659607e0Smrg	    goto connect_idle;
291659607e0Smrg    }
292659607e0Smrg#else
293659607e0Smrg    /*
294659607e0Smrg     * This is a simplified procedure; it simply toggles RTS.
295659607e0Smrg     */
296659607e0Smrg
297659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
298659607e0Smrg	return 0;
299659607e0Smrg    i |= XF86_M_DTR;		/* DTR = 1 */
300659607e0Smrg    i &= ~XF86_M_RTS;		/* RTS = 0 */
301659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
302659607e0Smrg	goto disconnect_idle;
303659607e0Smrg    usleep(200000);
304659607e0Smrg
305659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
306659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
307659607e0Smrg
3086aab59a7Smrg    /* wait for response */
309659607e0Smrg    xf86FlushInput(pInfo->fd);
310659607e0Smrg    i = XF86_M_DTR | XF86_M_RTS;	/* DTR = 1, RTS = 1 */
311659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
312659607e0Smrg
313659607e0Smrg    /* try to read something */
314659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
315659607e0Smrg        goto connect_idle;
316659607e0Smrg#endif
317659607e0Smrg
318659607e0Smrg    /* collect PnP COM device ID (2.1.7) */
319659607e0Smrg    i = 0;
320659607e0Smrg    *prePNP = FALSE;
321659607e0Smrg
322659607e0Smrg    usleep(200000);	/* the mouse must send `Begin ID' within 200msec */
323659607e0Smrg    while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
324659607e0Smrg	/* we may see "M", or "M3..." before `Begin ID' */
325659607e0Smrg	if (c == 'M')
326659607e0Smrg	    *prePNP = TRUE;
327659607e0Smrg
328659607e0Smrg        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
329659607e0Smrg	    *prePNP = FALSE;
330659607e0Smrg	    buf[0] = c;
331659607e0Smrg	    i = 1;
332659607e0Smrg	    break;
333659607e0Smrg        }
334659607e0Smrg	if (*prePNP)
335659607e0Smrg	    buf[i++] = c;
336659607e0Smrg
337659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
338659607e0Smrg	    break;
339659607e0Smrg    }
340659607e0Smrg    if (i <= 0) {
341659607e0Smrg	/* we haven't seen `Begin ID' in time... */
342659607e0Smrg	goto connect_idle;
343659607e0Smrg    }
344659607e0Smrg    if (*prePNP)
345659607e0Smrg	return i;
346659607e0Smrg
347659607e0Smrg    ++c;			/* make it `End ID' */
348659607e0Smrg    for (;;) {
349659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
350659607e0Smrg	    break;
351659607e0Smrg
352659607e0Smrg	xf86ReadSerial(pInfo->fd, &buf[i], 1);
353659607e0Smrg        if (buf[i++] == c)	/* End ID */
354659607e0Smrg	    break;
355659607e0Smrg	if (i >= 256)
356659607e0Smrg	    break;
357659607e0Smrg    }
358659607e0Smrg    if (buf[i - 1] != c)
359659607e0Smrg	goto connect_idle;
360659607e0Smrg    return i;
361659607e0Smrg
362659607e0Smrg    /*
363659607e0Smrg     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
364659607e0Smrg     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
365659607e0Smrg     * assuming there is something at the port even if it didn't
366659607e0Smrg     * respond to the PnP enumeration procedure.
367659607e0Smrg     */
368659607e0Smrgdisconnect_idle:
369659607e0Smrg    i = XF86_M_DTR | XF86_M_RTS;		/* DTR = 1, RTS = 1 */
370659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
371659607e0Smrgconnect_idle:
372659607e0Smrg    return 0;
373659607e0Smrg}
374659607e0Smrg
375659607e0Smrgstatic int
376659607e0Smrgpnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len)
377659607e0Smrg{
378659607e0Smrg    char s[3];
379659607e0Smrg    int offset;
380659607e0Smrg    int sum = 0;
381659607e0Smrg    int i, j;
382659607e0Smrg
383659607e0Smrg    id->revision = 0;
384659607e0Smrg    id->eisaid = NULL;
385659607e0Smrg    id->serial = NULL;
386659607e0Smrg    id->class = NULL;
387659607e0Smrg    id->compat = NULL;
388659607e0Smrg    id->description = NULL;
389659607e0Smrg    id->neisaid = 0;
390659607e0Smrg    id->nserial = 0;
391659607e0Smrg    id->nclass = 0;
392659607e0Smrg    id->ncompat = 0;
393659607e0Smrg    id->ndescription = 0;
394659607e0Smrg
395659607e0Smrg    offset = 0x28 - buf[0];
396659607e0Smrg
397659607e0Smrg    /* calculate checksum */
398659607e0Smrg    for (i = 0; i < len - 3; ++i) {
399659607e0Smrg	sum += buf[i];
400659607e0Smrg	buf[i] += offset;
401659607e0Smrg    }
402659607e0Smrg    sum += buf[len - 1];
403659607e0Smrg    for (; i < len; ++i)
404659607e0Smrg	buf[i] += offset;
405659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
406659607e0Smrg		len, len, buf);
407659607e0Smrg
408659607e0Smrg    /* revision */
409659607e0Smrg    buf[1] -= offset;
410659607e0Smrg    buf[2] -= offset;
411659607e0Smrg    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
412659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name,
413659607e0Smrg		id->revision / 100, id->revision % 100);
414659607e0Smrg
415659607e0Smrg    /* EISA vender and product ID */
416659607e0Smrg    id->eisaid = &buf[3];
417659607e0Smrg    id->neisaid = 7;
418659607e0Smrg
419659607e0Smrg    /* option strings */
420659607e0Smrg    i = 10;
421659607e0Smrg    if (buf[i] == '\\') {
422659607e0Smrg        /* device serial # */
423659607e0Smrg        for (j = ++i; i < len; ++i) {
424659607e0Smrg            if (buf[i] == '\\')
425659607e0Smrg		break;
426659607e0Smrg        }
427659607e0Smrg	if (i >= len)
428659607e0Smrg	    i -= 3;
429659607e0Smrg	if (i - j == 8) {
430659607e0Smrg            id->serial = &buf[j];
431659607e0Smrg            id->nserial = 8;
432659607e0Smrg	}
433659607e0Smrg    }
434659607e0Smrg    if (buf[i] == '\\') {
435659607e0Smrg        /* PnP class */
436659607e0Smrg        for (j = ++i; i < len; ++i) {
437659607e0Smrg            if (buf[i] == '\\')
438659607e0Smrg		break;
439659607e0Smrg        }
440659607e0Smrg	if (i >= len)
441659607e0Smrg	    i -= 3;
442659607e0Smrg	if (i > j + 1) {
443659607e0Smrg            id->class = &buf[j];
444659607e0Smrg            id->nclass = i - j;
445659607e0Smrg        }
446659607e0Smrg    }
447659607e0Smrg    if (buf[i] == '\\') {
448659607e0Smrg	/* compatible driver */
449659607e0Smrg        for (j = ++i; i < len; ++i) {
450659607e0Smrg            if (buf[i] == '\\')
451659607e0Smrg		break;
452659607e0Smrg        }
453659607e0Smrg	/*
454659607e0Smrg	 * PnP COM spec prior to v0.96 allowed '*' in this field,
455659607e0Smrg	 * it's not allowed now; just ignore it.
456659607e0Smrg	 */
457659607e0Smrg	if (buf[j] == '*')
458659607e0Smrg	    ++j;
459659607e0Smrg	if (i >= len)
460659607e0Smrg	    i -= 3;
461659607e0Smrg	if (i > j + 1) {
462659607e0Smrg            id->compat = &buf[j];
463659607e0Smrg            id->ncompat = i - j;
464659607e0Smrg        }
465659607e0Smrg    }
466659607e0Smrg    if (buf[i] == '\\') {
467659607e0Smrg	/* product description */
468659607e0Smrg        for (j = ++i; i < len; ++i) {
469659607e0Smrg            if (buf[i] == ';')
470659607e0Smrg		break;
471659607e0Smrg        }
472659607e0Smrg	if (i >= len)
473659607e0Smrg	    i -= 3;
474659607e0Smrg	if (i > j + 1) {
475659607e0Smrg            id->description = &buf[j];
476659607e0Smrg            id->ndescription = i - j;
477659607e0Smrg        }
478659607e0Smrg    }
479659607e0Smrg
480659607e0Smrg    /* checksum exists if there are any optional fields */
481659607e0Smrg    if ((id->nserial > 0) || (id->nclass > 0)
482659607e0Smrg	|| (id->ncompat > 0) || (id->ndescription > 0)) {
483659607e0Smrg	xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum);
484659607e0Smrg        sprintf(s, "%02X", sum & 0x0ff);
485659607e0Smrg        if (strncmp(s, &buf[len - 3], 2) != 0) {
486659607e0Smrg#if 0
487659607e0Smrg            /*
488659607e0Smrg	     * Checksum error!!
489659607e0Smrg	     * I found some mice do not comply with the PnP COM device
490659607e0Smrg	     * spec regarding checksum... XXX
491659607e0Smrg	     */
492659607e0Smrg	    return FALSE;
493659607e0Smrg#endif
494659607e0Smrg        }
495659607e0Smrg    }
496659607e0Smrg
497659607e0Smrg    return TRUE;
498659607e0Smrg}
499659607e0Smrg
500659607e0Smrg/* We can only identify MS at the moment */
501659607e0Smrgstatic MouseProtocolID
502659607e0Smrgprepnpparse(InputInfoPtr pInfo, char *buf)
503659607e0Smrg{
504659607e0Smrg    if (buf[0] == 'M' && buf[1] == '3')
505659607e0Smrg	return PROT_MS;
506659607e0Smrg    return PROT_UNKNOWN;
507659607e0Smrg}
508659607e0Smrg
509659607e0Smrg
510659607e0Smrgstatic symtab_t *
511659607e0Smrgpnpproto(pnpid_t *id)
512659607e0Smrg{
513659607e0Smrg    symtab_t *t;
514659607e0Smrg    int i, j;
515659607e0Smrg
516659607e0Smrg    if (id->nclass > 0)
517659607e0Smrg	if (strncmp(id->class, "MOUSE", id->nclass) != 0)
518659607e0Smrg	    /* this is not a mouse! */
519659607e0Smrg	    return NULL;
520659607e0Smrg
521659607e0Smrg    if (id->neisaid > 0) {
522659607e0Smrg        t = gettoken(pnpprod, id->eisaid, id->neisaid);
523659607e0Smrg	if (t->val != -1)
524659607e0Smrg            return t;
525659607e0Smrg    }
526659607e0Smrg
527659607e0Smrg    /*
528659607e0Smrg     * The 'Compatible drivers' field may contain more than one
529659607e0Smrg     * ID separated by ','.
530659607e0Smrg     */
531659607e0Smrg    if (id->ncompat <= 0)
532659607e0Smrg	return NULL;
533659607e0Smrg    for (i = 0; i < id->ncompat; ++i) {
534659607e0Smrg        for (j = i; id->compat[i] != ','; ++i)
535659607e0Smrg            if (i >= id->ncompat)
536659607e0Smrg		break;
537659607e0Smrg        if (i > j) {
538659607e0Smrg            t = gettoken(pnpprod, id->compat + j, i - j);
539659607e0Smrg	    if (t->val != -1)
540659607e0Smrg                return t;
541659607e0Smrg	}
542659607e0Smrg    }
543659607e0Smrg
544659607e0Smrg    return NULL;
545659607e0Smrg}
546659607e0Smrg
547659607e0Smrg/* name/val mapping */
548659607e0Smrg
549659607e0Smrgstatic symtab_t *
5506aab59a7Smrggettoken(symtab_t *tab, char *s, int len)
551659607e0Smrg{
552659607e0Smrg    int i;
553659607e0Smrg
554659607e0Smrg    for (i = 0; tab[i].name != NULL; ++i) {
555659607e0Smrg	if (strncmp(tab[i].name, s, len) == 0)
556659607e0Smrg	    break;
557659607e0Smrg    }
558659607e0Smrg    return &tab[i];
559659607e0Smrg}
560659607e0Smrg
561659607e0Smrg/******************* PS/2 PnP probing ****************/
562659607e0Smrg
563659607e0Smrgstatic int
564659607e0SmrgreadMouse(InputInfoPtr pInfo, unsigned char *u)
565659607e0Smrg{
566659607e0Smrg
567659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
568659607e0Smrg	return FALSE;
569659607e0Smrg
570659607e0Smrg    xf86ReadSerial(pInfo->fd, u, 1);
571659607e0Smrg    return TRUE;
572659607e0Smrg}
573659607e0Smrg
574659607e0Smrgstatic void
575659607e0Smrgps2DisableWrapMode(InputInfoPtr pInfo)
576659607e0Smrg{
577659607e0Smrg    unsigned char reset_wrap_mode[] = { 0xEC };
578659607e0Smrg    ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
579659607e0Smrg}
580659607e0Smrg
581659607e0SmrgBool
582659607e0Smrgps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len)
583659607e0Smrg{
584659607e0Smrg    unsigned char c;
585659607e0Smrg    int i,j;
586659607e0Smrg
587659607e0Smrg#ifdef DEBUG
588659607e0Smrg    xf86ErrorF("Ps/2 data package:");
589659607e0Smrg    for (i = 0; i < len; i++)
590659607e0Smrg	xf86ErrorF(" %x", *(bytes + i));
591659607e0Smrg    xf86ErrorF("\n");
592659607e0Smrg#endif
593659607e0Smrg
594659607e0Smrg    for (i = 0; i < len; i++) {
595659607e0Smrg	for (j = 0; j < 10; j++) {
596659607e0Smrg	    xf86WriteSerial(pInfo->fd, bytes + i, 1);
597659607e0Smrg	    usleep(10000);
598659607e0Smrg	    if (!readMouse(pInfo,&c)) {
599659607e0Smrg#ifdef DEBUG
600659607e0Smrg		xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i));
601659607e0Smrg#endif
602659607e0Smrg		return FALSE;
603659607e0Smrg	    }
604659607e0Smrg#ifdef DEBUG
605659607e0Smrg	    xf86ErrorF("Recieved: 0x%x\n",c);
606659607e0Smrg#endif
607659607e0Smrg	    if (c == 0xFA) /* ACK */
608659607e0Smrg		break;
609659607e0Smrg
610659607e0Smrg	    if (c == 0xFE) /* resend */
611659607e0Smrg		continue;
612659607e0Smrg
613659607e0Smrg
614659607e0Smrg	    if (c == 0xFC) /* error */
615659607e0Smrg		return FALSE;
616659607e0Smrg
617659607e0Smrg	    /* Some mice accidently enter wrap mode during init */
618659607e0Smrg	    if (c == *(bytes + i)    /* wrap mode */
619659607e0Smrg		&& (*(bytes + i) != 0xEC)) /* avoid recursion */
620659607e0Smrg		ps2DisableWrapMode(pInfo);
621659607e0Smrg
622659607e0Smrg	    return FALSE;
623659607e0Smrg	}
624659607e0Smrg	if (j == 10)
625659607e0Smrg	    return FALSE;
626659607e0Smrg    }
627659607e0Smrg
628659607e0Smrg    return TRUE;
629659607e0Smrg}
630659607e0Smrg
631659607e0Smrgstatic Bool
632659607e0Smrgps2DisableDataReporting(InputInfoPtr pInfo)
633659607e0Smrg{
634659607e0Smrg    unsigned char packet[] = { 0xF5 };
635659607e0Smrg    return ps2SendPacket(pInfo, packet, sizeof(packet));
636659607e0Smrg}
637659607e0Smrg
638659607e0SmrgBool
639659607e0Smrgps2EnableDataReporting(InputInfoPtr pInfo)
640659607e0Smrg{
641659607e0Smrg    unsigned char packet[] = { 0xF4 };
642659607e0Smrg    return ps2SendPacket(pInfo, packet, sizeof(packet));
643659607e0Smrg}
644659607e0Smrg
645659607e0Smrgint
646659607e0Smrgps2GetDeviceID(InputInfoPtr pInfo)
647659607e0Smrg{
648659607e0Smrg    unsigned char u;
649659607e0Smrg    unsigned char packet[] = { 0xf2 };
650659607e0Smrg
651659607e0Smrg    usleep(30000);
652659607e0Smrg    xf86FlushInput(pInfo->fd);
653659607e0Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
654659607e0Smrg	return -1;
655659607e0Smrg    while (1) {
656659607e0Smrg	if (!readMouse(pInfo,&u))
657659607e0Smrg	    return -1;
658659607e0Smrg	if (u != 0xFA)
659659607e0Smrg	    break;
660659607e0Smrg    }
661659607e0Smrg#ifdef DEBUG
662659607e0Smrg    xf86ErrorF("Obtained Mouse Type: %x\n",u);
663659607e0Smrg#endif
664659607e0Smrg    return (int) u;
665659607e0Smrg}
666659607e0Smrg
667659607e0SmrgBool
668659607e0Smrgps2Reset(InputInfoPtr pInfo)
669659607e0Smrg{
670659607e0Smrg    unsigned char u;
671659607e0Smrg    unsigned char packet[] = { 0xff };
672659607e0Smrg    unsigned char reply[] = { 0xaa, 0x00 };
673659607e0Smrg    unsigned int i;
674659607e0Smrg#ifdef DEBUG
675659607e0Smrg   xf86ErrorF("PS/2 Mouse reset\n");
676659607e0Smrg#endif
677659607e0Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
678659607e0Smrg	return FALSE;
679659607e0Smrg    /* we need a little delay here */
680659607e0Smrg    xf86WaitForInput(pInfo->fd, 500000);
681659607e0Smrg    for (i = 0; i < sizeof(reply) ; i++) {
682659607e0Smrg	if (!readMouse(pInfo,&u)) {
683659607e0Smrg	    goto EXIT;
684659607e0Smrg	}
685659607e0Smrg	if (u != reply[i])
686659607e0Smrg	    goto EXIT;
687659607e0Smrg    }
688659607e0Smrg    return TRUE;
689659607e0Smrg
690659607e0Smrg EXIT:
691659607e0Smrg    xf86FlushInput(pInfo->fd);
692659607e0Smrg    return FALSE;
693659607e0Smrg}
694659607e0Smrg
695659607e0Smrgstatic MouseProtocolID
696659607e0SmrgprobePs2ProtocolPnP(InputInfoPtr pInfo)
697659607e0Smrg{
698659607e0Smrg    unsigned char u;
699659607e0Smrg    MouseProtocolID ret = PROT_UNKNOWN;
700659607e0Smrg
701659607e0Smrg    xf86FlushInput(pInfo->fd);
702659607e0Smrg
703659607e0Smrg    ps2DisableDataReporting(pInfo);
704659607e0Smrg
705659607e0Smrg    if (ps2Reset(pInfo)) { /* Reset PS2 device */
706659607e0Smrg  	unsigned char seq[] = { 243, 200, 243, 100, 243, 80 };
707659607e0Smrg	/* Try to identify Intelli Mouse */
708659607e0Smrg	if (ps2SendPacket(pInfo, seq, sizeof(seq))) {
709659607e0Smrg	    u = ps2GetDeviceID(pInfo);
710659607e0Smrg	    if (u == 0x03) {
711659607e0Smrg		/* found IntelliMouse now try IntelliExplorer */
712659607e0Smrg		unsigned char seq[] = { 243, 200, 243, 200, 243, 80 };
713659607e0Smrg		if (ps2SendPacket(pInfo,seq,sizeof(seq))) {
714659607e0Smrg		    u = ps2GetDeviceID(pInfo);
715659607e0Smrg		    if (u == 0x04)
716659607e0Smrg			ret =  PROT_EXPPS2;
717659607e0Smrg		    else
718659607e0Smrg			ret = PROT_IMPS2;
719659607e0Smrg		}
720659607e0Smrg	    } else if (ps2Reset(pInfo))  /* reset again to find sane state */
721659607e0Smrg		ret = PROT_PS2;
722659607e0Smrg	}
723659607e0Smrg
724659607e0Smrg	if (ret != PROT_UNKNOWN)
725659607e0Smrg	    ps2EnableDataReporting(pInfo);
726659607e0Smrg    }
727659607e0Smrg    return ret;
728659607e0Smrg}
729659607e0Smrg
730659607e0Smrgstatic struct ps2protos {
731659607e0Smrg    int Id;
732659607e0Smrg    MouseProtocolID protoID;
733659607e0Smrg} ps2 [] = {
734659607e0Smrg    { 0x0, PROT_PS2 },
735659607e0Smrg    { 0x3, PROT_IMPS2 },
736659607e0Smrg    { 0x4, PROT_EXPPS2 },
737659607e0Smrg    { -1 , PROT_UNKNOWN }
738659607e0Smrg};
739659607e0Smrg
740659607e0Smrg
741659607e0Smrgstatic MouseProtocolID
742659607e0SmrggetPs2ProtocolPnP(InputInfoPtr pInfo)
743659607e0Smrg{
744659607e0Smrg    int Id;
745659607e0Smrg    int i;
746659607e0Smrg    MouseProtocolID proto;
747659607e0Smrg    int count = 4;
748659607e0Smrg
749659607e0Smrg    xf86FlushInput(pInfo->fd);
750659607e0Smrg
751659607e0Smrg    while (--count)
752659607e0Smrg	if (ps2DisableDataReporting(pInfo))
753659607e0Smrg	    break;
754659607e0Smrg
755659607e0Smrg    if (!count) {
756659607e0Smrg	proto = PROT_UNKNOWN;
757659607e0Smrg	goto EXIT;
758659607e0Smrg    }
759659607e0Smrg
760659607e0Smrg    if ((Id = ps2GetDeviceID(pInfo)) == -1) {
761659607e0Smrg	proto = PROT_UNKNOWN;
762659607e0Smrg	goto EXIT;
763659607e0Smrg    }
764659607e0Smrg
765659607e0Smrg    if (-1 == ps2EnableDataReporting(pInfo)) {
766659607e0Smrg	proto = PROT_UNKNOWN;
767659607e0Smrg	goto EXIT;
768659607e0Smrg    }
769659607e0Smrg
770659607e0Smrg    for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) {
771659607e0Smrg	if (ps2[i].Id == Id) {
772659607e0Smrg	    xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id);
773659607e0Smrg	    proto =  ps2[i].protoID;
774659607e0Smrg	    goto EXIT;
775659607e0Smrg	}
776659607e0Smrg    }
777659607e0Smrg
778659607e0Smrg    proto = PROT_UNKNOWN;
779659607e0Smrg    xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id);
780659607e0Smrg
781659607e0Smrg EXIT:
782659607e0Smrg    xf86FlushInput(pInfo->fd);
783659607e0Smrg    return proto;
784659607e0Smrg}
785659607e0Smrg
786