pnp.c revision 6aab59a7
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
27659607e0Smrg#include <stdio.h>
28659607e0Smrg#include <string.h>
29659607e0Smrg#include <unistd.h>
30659607e0Smrg#define NEED_EVENTS
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 */
78659607e0Smrg    { "LGI800C",  PROT_IMSERIAL },	/* Logitech MouseMan (4 button model) */
79659607e0Smrg    { "LGI8033",  PROT_IMSERIAL },	/* Logitech Cordless MouseMan Wheel */
80659607e0Smrg    { "LGI8050",  PROT_IMSERIAL },	/* Logitech MouseMan+ */
81659607e0Smrg    { "LGI8051",  PROT_IMSERIAL },	/* Logitech FirstMouse+ */
82659607e0Smrg    { "LGI8001",  PROT_LOGIMAN },	/* Logitech serial */
83659607e0Smrg    { "A4W0005",  PROT_IMSERIAL },	/* A4 Tech 4D/4D+ Mouse */
84659607e0Smrg    { "PEC9802",  PROT_IMSERIAL },	/* 8D Scroll Mouse */
85659607e0Smrg
86659607e0Smrg    { "PNP0F00",  PROT_BM },		/* MS bus */
87659607e0Smrg    { "PNP0F01",  PROT_MS },		/* MS serial */
88659607e0Smrg    { "PNP0F02",  PROT_BM },		/* MS InPort */
89659607e0Smrg    { "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     */
94659607e0Smrg    { "PNP0F04",  PROT_MSC },		/* MouseSystems */
95659607e0Smrg    { "PNP0F05",  PROT_MSC },		/* MouseSystems */
96659607e0Smrg#ifdef notyet
97659607e0Smrg    { "PNP0F06",  PROT_??? },		/* Genius Mouse */
98659607e0Smrg    { "PNP0F07",  PROT_??? },		/* Genius Mouse */
99659607e0Smrg#endif
100659607e0Smrg    { "PNP0F08",  PROT_LOGIMAN },	/* Logitech serial */
101659607e0Smrg    { "PNP0F09",  PROT_MS },		/* MS BallPoint serial */
102659607e0Smrg    { "PNP0F0A",  PROT_MS },		/* MS PnP serial */
103659607e0Smrg    { "PNP0F0B",  PROT_MS },		/* MS PnP BallPoint serial */
1046aab59a7Smrg    { "PNP0F0C",  PROT_MS },		/* MS serial compatible */
1056aab59a7Smrg    { "PNP0F0D",  PROT_BM },		/* MS InPort compatible */
1066aab59a7Smrg    { "PNP0F0E",  PROT_PS2 },		/* MS PS/2 compatible */
1076aab59a7Smrg    { "PNP0F0F",  PROT_MS },		/* MS BallPoint compatible */
108659607e0Smrg#ifdef notyet
109659607e0Smrg    { "PNP0F10",  PROT_??? },		/* TI QuickPort */
110659607e0Smrg#endif
1116aab59a7Smrg    { "PNP0F11",  PROT_BM },		/* MS bus compatible */
112659607e0Smrg    { "PNP0F12",  PROT_PS2 },		/* Logitech PS/2 */
113659607e0Smrg    { "PNP0F13",  PROT_PS2 },		/* PS/2 */
114659607e0Smrg#ifdef notyet
115659607e0Smrg    { "PNP0F14",  PROT_??? },		/* MS Kids Mouse */
116659607e0Smrg#endif
117659607e0Smrg    { "PNP0F15",  PROT_BM },		/* Logitech bus */
118659607e0Smrg#ifdef notyet
119659607e0Smrg    { "PNP0F16",  PROT_??? },		/* Logitech SWIFT */
120659607e0Smrg#endif
121659607e0Smrg    { "PNP0F17",  PROT_LOGIMAN },	/* Logitech serial compat */
122659607e0Smrg    { "PNP0F18",  PROT_BM },		/* Logitech bus compatible */
123659607e0Smrg    { "PNP0F19",  PROT_PS2 },		/* Logitech PS/2 compatible */
124659607e0Smrg#ifdef notyet
125659607e0Smrg    { "PNP0F1A",  PROT_??? },		/* Logitech SWIFT compatible */
126659607e0Smrg    { "PNP0F1B",  PROT_??? },		/* HP Omnibook */
127659607e0Smrg    { "PNP0F1C",  PROT_??? },		/* Compaq LTE TrackBall PS/2 */
128659607e0Smrg    { "PNP0F1D",  PROT_??? },		/* Compaq LTE TrackBall serial */
129659607e0Smrg    { "PNP0F1E",  PROT_??? },		/* MS Kids Trackball */
130659607e0Smrg#endif
131659607e0Smrg    { NULL,	  PROT_UNKNOWN },
132659607e0Smrg};
133659607e0Smrg
134659607e0Smrgstatic const char *pnpSerial[] = {
135659607e0Smrg	"BaudRate",	"1200",
136659607e0Smrg	"DataBits",	"7",
137659607e0Smrg	"StopBits",	"1",
138659607e0Smrg	"Parity",	"None",
139659607e0Smrg	"FlowControl",	"None",
140659607e0Smrg	"VTime",	"0",
141659607e0Smrg	"VMin",		"1",
142659607e0Smrg	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{
156659607e0Smrg    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
162659607e0Smrg    if ((len = pnpgets(pInfo, buf, &prePNP)) > 0)
163659607e0Smrg    {
164659607e0Smrg	if (!prePNP) {
165659607e0Smrg	    if (pnpparse(pInfo, &pnpid, buf, len) &&
166659607e0Smrg		(t = pnpproto(&pnpid)) != NULL) {
167659607e0Smrg		xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n",
168659607e0Smrg			    pInfo->name, t->val);
169659607e0Smrg		return (t->val);
170659607e0Smrg	    }
171659607e0Smrg	} else
172659607e0Smrg	    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;
184659607e0Smrg
185659607e0Smrg    if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) {
186659607e0Smrg	if (val == MouseGetSerialPnpProtocol(pInfo))
187659607e0Smrg	    return val;
188659607e0Smrg    }
189659607e0Smrg
190659607e0Smrg    last = mPriv->pnpLast;
191659607e0Smrg    mPriv->pnpLast = currentTime.milliseconds;
192659607e0Smrg
193659607e0Smrg    if (last) {
194659607e0Smrg	if (last - currentTime.milliseconds < 100
195659607e0Smrg	    || (mPriv->disablePnPauto
196659607e0Smrg		&& (last - currentTime.milliseconds < 10000))) {
197659607e0Smrg#ifdef EXTMOUSEDEBUG
198659607e0Smrg	    xf86ErrorF("Mouse: Disabling PnP\n");
199659607e0Smrg#endif
200659607e0Smrg	    mPriv->disablePnPauto = TRUE;
201659607e0Smrg	    return PROT_UNKNOWN;
202659607e0Smrg	}
203659607e0Smrg    }
204659607e0Smrg
205659607e0Smrg#ifdef EXTMOUSEDEBUG
206659607e0Smrg    if (mPriv->disablePnPauto)
207659607e0Smrg	xf86ErrorF("Mouse: Enabling PnP\n");
208659607e0Smrg#endif
209659607e0Smrg    mPriv->disablePnPauto = FALSE;
210659607e0Smrg
211659607e0Smrg    if (mPriv->soft)
212659607e0Smrg	return getPs2ProtocolPnP(pInfo);
213659607e0Smrg    else
214659607e0Smrg	return probePs2ProtocolPnP(pInfo);
215659607e0Smrg}
216659607e0Smrg
217659607e0Smrg/*
218659607e0Smrg * Try to elicit a PnP ID as described in
219659607e0Smrg * 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
224659607e0Smrg * the driver software monitors the com port for dynamic connection or
225659607e0Smrg * removal of a device at the port, because `moused' simply quits if no
226659607e0Smrg * device is found.
227659607e0Smrg *
228659607e0Smrg * In addition, as PnP COM device enumeration procedure slightly has
229659607e0Smrg * changed since its first publication, devices which follow earlier
230659607e0Smrg * 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;
239659607e0Smrg
240659607e0Smrg#if 0
241659607e0Smrg    /*
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)
249659607e0Smrg	return 0;
250659607e0Smrg    i |= XF86_M_DTR;		/* DTR = 1 */
251659607e0Smrg    i &= ~XF86_M_RTS;		/* RTS = 0 */
252659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
253659607e0Smrg	goto disconnect_idle;
254659607e0Smrg    usleep(200000);
255659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
256659607e0Smrg	(i & XF86_M_DSR) == 0)
257659607e0Smrg	goto disconnect_idle;
258659607e0Smrg
259659607e0Smrg    /* port setup, 1st phase (2.1.3) */
260659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
261659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
262659607e0Smrg    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
263659607e0Smrg    xf86SerialModemClearBits(pInfo->fd, i);
264659607e0Smrg    usleep(200000);
265659607e0Smrg    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);
271659607e0Smrg    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
277659607e0Smrg	/* port setup, 2nd phase (2.1.5) */
278659607e0Smrg        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
279659607e0Smrg	xf86SerialModemClearBits(pInfo->fd, i);
280659607e0Smrg        usleep(200000);
281659607e0Smrg
2826aab59a7Smrg	/* wait for response, 2nd phase (2.1.6) */
283659607e0Smrg	xf86FlushInput(pInfo->fd);
284659607e0Smrg        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
285659607e0Smrg	xf86SerialModemSetBits(pInfo->fd, i);
286659607e0Smrg
287659607e0Smrg        /* try to read something */
288659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
289659607e0Smrg	    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)
297659607e0Smrg	return 0;
298659607e0Smrg    i |= XF86_M_DTR;		/* DTR = 1 */
299659607e0Smrg    i &= ~XF86_M_RTS;		/* RTS = 0 */
300659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
301659607e0Smrg	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);
309659607e0Smrg    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;
320659607e0Smrg
321659607e0Smrg    usleep(200000);	/* the mouse must send `Begin ID' within 200msec */
322659607e0Smrg    while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
323659607e0Smrg	/* we may see "M", or "M3..." before `Begin ID' */
324659607e0Smrg	if (c == 'M')
325659607e0Smrg	    *prePNP = TRUE;
326659607e0Smrg
327659607e0Smrg        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
328659607e0Smrg	    *prePNP = FALSE;
329659607e0Smrg	    buf[0] = c;
330659607e0Smrg	    i = 1;
331659607e0Smrg	    break;
332659607e0Smrg        }
333659607e0Smrg	if (*prePNP)
334659607e0Smrg	    buf[i++] = c;
335659607e0Smrg
336659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
337659607e0Smrg	    break;
338659607e0Smrg    }
339659607e0Smrg    if (i <= 0) {
340659607e0Smrg	/* we haven't seen `Begin ID' in time... */
341659607e0Smrg	goto connect_idle;
342659607e0Smrg    }
343659607e0Smrg    if (*prePNP)
344659607e0Smrg	return i;
345659607e0Smrg
346659607e0Smrg    ++c;			/* make it `End ID' */
347659607e0Smrg    for (;;) {
348659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
349659607e0Smrg	    break;
350659607e0Smrg
351659607e0Smrg	xf86ReadSerial(pInfo->fd, &buf[i], 1);
352659607e0Smrg        if (buf[i++] == c)	/* End ID */
353659607e0Smrg	    break;
354659607e0Smrg	if (i >= 256)
355659607e0Smrg	    break;
356659607e0Smrg    }
357659607e0Smrg    if (buf[i - 1] != c)
358659607e0Smrg	goto connect_idle;
359659607e0Smrg    return i;
360659607e0Smrg
361659607e0Smrg    /*
362659607e0Smrg     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
363659607e0Smrg     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
364659607e0Smrg     * assuming there is something at the port even if it didn't
365659607e0Smrg     * respond to the PnP enumeration procedure.
366659607e0Smrg     */
367659607e0Smrgdisconnect_idle:
368659607e0Smrg    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) {
398659607e0Smrg	sum += buf[i];
399659607e0Smrg	buf[i] += offset;
400659607e0Smrg    }
401659607e0Smrg    sum += buf[len - 1];
402659607e0Smrg    for (; i < len; ++i)
403659607e0Smrg	buf[i] += offset;
404659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
405659607e0Smrg		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,
412659607e0Smrg		id->revision / 100, id->revision % 100);
413659607e0Smrg
414659607e0Smrg    /* EISA vender 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] == '\\')
424659607e0Smrg		break;
425659607e0Smrg        }
426659607e0Smrg	if (i >= len)
427659607e0Smrg	    i -= 3;
428659607e0Smrg	if (i - j == 8) {
429659607e0Smrg            id->serial = &buf[j];
430659607e0Smrg            id->nserial = 8;
431659607e0Smrg	}
432659607e0Smrg    }
433659607e0Smrg    if (buf[i] == '\\') {
434659607e0Smrg        /* PnP class */
435659607e0Smrg        for (j = ++i; i < len; ++i) {
436659607e0Smrg            if (buf[i] == '\\')
437659607e0Smrg		break;
438659607e0Smrg        }
439659607e0Smrg	if (i >= len)
440659607e0Smrg	    i -= 3;
441659607e0Smrg	if (i > j + 1) {
442659607e0Smrg            id->class = &buf[j];
443659607e0Smrg            id->nclass = i - j;
444659607e0Smrg        }
445659607e0Smrg    }
446659607e0Smrg    if (buf[i] == '\\') {
447659607e0Smrg	/* compatible driver */
448659607e0Smrg        for (j = ++i; i < len; ++i) {
449659607e0Smrg            if (buf[i] == '\\')
450659607e0Smrg		break;
451659607e0Smrg        }
452659607e0Smrg	/*
453659607e0Smrg	 * PnP COM spec prior to v0.96 allowed '*' in this field,
454659607e0Smrg	 * it's not allowed now; just ignore it.
455659607e0Smrg	 */
456659607e0Smrg	if (buf[j] == '*')
457659607e0Smrg	    ++j;
458659607e0Smrg	if (i >= len)
459659607e0Smrg	    i -= 3;
460659607e0Smrg	if (i > j + 1) {
461659607e0Smrg            id->compat = &buf[j];
462659607e0Smrg            id->ncompat = i - j;
463659607e0Smrg        }
464659607e0Smrg    }
465659607e0Smrg    if (buf[i] == '\\') {
466659607e0Smrg	/* product description */
467659607e0Smrg        for (j = ++i; i < len; ++i) {
468659607e0Smrg            if (buf[i] == ';')
469659607e0Smrg		break;
470659607e0Smrg        }
471659607e0Smrg	if (i >= len)
472659607e0Smrg	    i -= 3;
473659607e0Smrg	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)
481659607e0Smrg	|| (id->ncompat > 0) || (id->ndescription > 0)) {
482659607e0Smrg	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
486659607e0Smrg            /*
487659607e0Smrg	     * Checksum error!!
488659607e0Smrg	     * I found some mice do not comply with the PnP COM device
489659607e0Smrg	     * spec regarding checksum... XXX
490659607e0Smrg	     */
491659607e0Smrg	    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')
504659607e0Smrg	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)
516659607e0Smrg	if (strncmp(id->class, "MOUSE", id->nclass) != 0)
517659607e0Smrg	    /* this is not a mouse! */
518659607e0Smrg	    return NULL;
519659607e0Smrg
520659607e0Smrg    if (id->neisaid > 0) {
521659607e0Smrg        t = gettoken(pnpprod, id->eisaid, id->neisaid);
522659607e0Smrg	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)
531659607e0Smrg	return NULL;
532659607e0Smrg    for (i = 0; i < id->ncompat; ++i) {
533659607e0Smrg        for (j = i; id->compat[i] != ','; ++i)
534659607e0Smrg            if (i >= id->ncompat)
535659607e0Smrg		break;
536659607e0Smrg        if (i > j) {
537659607e0Smrg            t = gettoken(pnpprod, id->compat + j, i - j);
538659607e0Smrg	    if (t->val != -1)
539659607e0Smrg                return t;
540659607e0Smrg	}
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) {
554659607e0Smrg	if (strncmp(tab[i].name, s, len) == 0)
555659607e0Smrg	    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)
567659607e0Smrg	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;
585659607e0Smrg
586659607e0Smrg#ifdef DEBUG
587659607e0Smrg    xf86ErrorF("Ps/2 data package:");
588659607e0Smrg    for (i = 0; i < len; i++)
589659607e0Smrg	xf86ErrorF(" %x", *(bytes + i));
590659607e0Smrg    xf86ErrorF("\n");
591659607e0Smrg#endif
592659607e0Smrg
593659607e0Smrg    for (i = 0; i < len; i++) {
594659607e0Smrg	for (j = 0; j < 10; j++) {
595659607e0Smrg	    xf86WriteSerial(pInfo->fd, bytes + i, 1);
596659607e0Smrg	    usleep(10000);
597659607e0Smrg	    if (!readMouse(pInfo,&c)) {
598659607e0Smrg#ifdef DEBUG
599659607e0Smrg		xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i));
600659607e0Smrg#endif
601659607e0Smrg		return FALSE;
602659607e0Smrg	    }
603659607e0Smrg#ifdef DEBUG
604659607e0Smrg	    xf86ErrorF("Recieved: 0x%x\n",c);
605659607e0Smrg#endif
606659607e0Smrg	    if (c == 0xFA) /* ACK */
607659607e0Smrg		break;
608659607e0Smrg
609659607e0Smrg	    if (c == 0xFE) /* resend */
610659607e0Smrg		continue;
611659607e0Smrg
612659607e0Smrg
613659607e0Smrg	    if (c == 0xFC) /* error */
614659607e0Smrg		return FALSE;
615659607e0Smrg
616659607e0Smrg	    /* Some mice accidently enter wrap mode during init */
617659607e0Smrg	    if (c == *(bytes + i)    /* wrap mode */
618659607e0Smrg		&& (*(bytes + i) != 0xEC)) /* avoid recursion */
619659607e0Smrg		ps2DisableWrapMode(pInfo);
620659607e0Smrg
621659607e0Smrg	    return FALSE;
622659607e0Smrg	}
623659607e0Smrg	if (j == 10)
624659607e0Smrg	    return FALSE;
625659607e0Smrg    }
626659607e0Smrg
627659607e0Smrg    return TRUE;
628659607e0Smrg}
629659607e0Smrg
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);
652659607e0Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
653659607e0Smrg	return -1;
654659607e0Smrg    while (1) {
655659607e0Smrg	if (!readMouse(pInfo,&u))
656659607e0Smrg	    return -1;
657659607e0Smrg	if (u != 0xFA)
658659607e0Smrg	    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
676659607e0Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
677659607e0Smrg	return FALSE;
678659607e0Smrg    /* we need a little delay here */
679659607e0Smrg    xf86WaitForInput(pInfo->fd, 500000);
680659607e0Smrg    for (i = 0; i < sizeof(reply) ; i++) {
681659607e0Smrg	if (!readMouse(pInfo,&u)) {
682659607e0Smrg	    goto EXIT;
683659607e0Smrg	}
684659607e0Smrg	if (u != reply[i])
685659607e0Smrg	    goto EXIT;
686659607e0Smrg    }
687659607e0Smrg    return TRUE;
688659607e0Smrg
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);
703659607e0Smrg
704659607e0Smrg    if (ps2Reset(pInfo)) { /* Reset PS2 device */
705659607e0Smrg  	unsigned char seq[] = { 243, 200, 243, 100, 243, 80 };
706659607e0Smrg	/* Try to identify Intelli Mouse */
707659607e0Smrg	if (ps2SendPacket(pInfo, seq, sizeof(seq))) {
708659607e0Smrg	    u = ps2GetDeviceID(pInfo);
709659607e0Smrg	    if (u == 0x03) {
710659607e0Smrg		/* found IntelliMouse now try IntelliExplorer */
711659607e0Smrg		unsigned char seq[] = { 243, 200, 243, 200, 243, 80 };
712659607e0Smrg		if (ps2SendPacket(pInfo,seq,sizeof(seq))) {
713659607e0Smrg		    u = ps2GetDeviceID(pInfo);
714659607e0Smrg		    if (u == 0x04)
715659607e0Smrg			ret =  PROT_EXPPS2;
716659607e0Smrg		    else
717659607e0Smrg			ret = PROT_IMPS2;
718659607e0Smrg		}
719659607e0Smrg	    } else if (ps2Reset(pInfo))  /* reset again to find sane state */
720659607e0Smrg		ret = PROT_PS2;
721659607e0Smrg	}
722659607e0Smrg
723659607e0Smrg	if (ret != PROT_UNKNOWN)
724659607e0Smrg	    ps2EnableDataReporting(pInfo);
725659607e0Smrg    }
726659607e0Smrg    return ret;
727659607e0Smrg}
728659607e0Smrg
729659607e0Smrgstatic struct ps2protos {
730659607e0Smrg    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)
751659607e0Smrg	if (ps2DisableDataReporting(pInfo))
752659607e0Smrg	    break;
753659607e0Smrg
754659607e0Smrg    if (!count) {
755659607e0Smrg	proto = PROT_UNKNOWN;
756659607e0Smrg	goto EXIT;
757659607e0Smrg    }
758659607e0Smrg
759659607e0Smrg    if ((Id = ps2GetDeviceID(pInfo)) == -1) {
760659607e0Smrg	proto = PROT_UNKNOWN;
761659607e0Smrg	goto EXIT;
762659607e0Smrg    }
763659607e0Smrg
764659607e0Smrg    if (-1 == ps2EnableDataReporting(pInfo)) {
765659607e0Smrg	proto = PROT_UNKNOWN;
766659607e0Smrg	goto EXIT;
767659607e0Smrg    }
768659607e0Smrg
769659607e0Smrg    for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) {
770659607e0Smrg	if (ps2[i].Id == Id) {
771659607e0Smrg	    xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id);
772659607e0Smrg	    proto =  ps2[i].protoID;
773659607e0Smrg	    goto EXIT;
774659607e0Smrg	}
775659607e0Smrg    }
776659607e0Smrg
777659607e0Smrg    proto = PROT_UNKNOWN;
778659607e0Smrg    xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id);
779659607e0Smrg
780659607e0Smrg EXIT:
781659607e0Smrg    xf86FlushInput(pInfo->fd);
782659607e0Smrg    return proto;
783659607e0Smrg}
784659607e0Smrg
785