pnp.c revision 659607e0
1659607e0Smrg/* $XFree86: xc/programs/Xserver/hw/xfree86/input/mouse/pnp.c,v 1.20tsi Exp $ */
2659607e0Smrg/*
3659607e0Smrg * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
4659607e0Smrg *
5659607e0Smrg * Permission to use, copy, modify, distribute, and sell this software and its
6659607e0Smrg * documentation for any purpose is hereby granted without fee, provided that
7659607e0Smrg * the above copyright notice appear in all copies and that both that
8659607e0Smrg * copyright notice and this permission notice appear in supporting
9659607e0Smrg * documentation, and that the name of Kazutaka YOKOTA not be used in
10659607e0Smrg * advertising or publicity pertaining to distribution of the software without
11659607e0Smrg * specific, written prior permission.  Kazutaka YOKOTA makes no representations
12659607e0Smrg * about the suitability of this software for any purpose.  It is provided
13659607e0Smrg * "as is" without express or implied warranty.
14659607e0Smrg *
15659607e0Smrg * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16659607e0Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17659607e0Smrg * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18659607e0Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19659607e0Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20659607e0Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21659607e0Smrg * PERFORMANCE OF THIS SOFTWARE.
22659607e0Smrg */
23659607e0Smrg
24659607e0Smrg#ifdef HAVE_CONFIG_H
25659607e0Smrg#include "config.h"
26659607e0Smrg#endif
27659607e0Smrg
28659607e0Smrg#include <stdio.h>
29659607e0Smrg#include <string.h>
30659607e0Smrg#include <unistd.h>
31659607e0Smrg#define NEED_EVENTS
32659607e0Smrg#include <X11/X.h>
33659607e0Smrg#include <X11/Xproto.h>
34659607e0Smrg#include "inputstr.h"
35659607e0Smrg#include "scrnintstr.h"
36659607e0Smrg#include "xf86.h"
37659607e0Smrg#include "xf86Priv.h"
38659607e0Smrg#include "xf86Xinput.h"
39659607e0Smrg#include "xf86_OSproc.h"
40659607e0Smrg#include "xf86OSmouse.h"
41659607e0Smrg#include "mouse.h"
42659607e0Smrg#include "mousePriv.h"
43659607e0Smrg
44659607e0Smrg#ifdef MOUSEINITDEBUG
45659607e0Smrg# define DEBUG
46659607e0Smrg# define EXTMOUSEDEBUG
47659607e0Smrg#endif
48659607e0Smrg
49659607e0Smrg/* serial PnP ID string */
50659607e0Smrgtypedef struct {
51659607e0Smrg    int revision;	/* PnP revision, 100 for 1.00 */
52659607e0Smrg    char *eisaid;	/* EISA ID including mfr ID and product ID */
53659607e0Smrg    char *serial;	/* serial No, optional */
54659607e0Smrg    char *class;	/* device class, optional */
55659607e0Smrg    char *compat;	/* list of compatible drivers, optional */
56659607e0Smrg    char *description;	/* product description, optional */
57659607e0Smrg    int neisaid;	/* length of the above fields... */
58659607e0Smrg    int nserial;
59659607e0Smrg    int nclass;
60659607e0Smrg    int ncompat;
61659607e0Smrg    int ndescription;
62659607e0Smrg} pnpid_t;
63659607e0Smrg
64659607e0Smrg/* symbol table entry */
65659607e0Smrgtypedef struct {
66659607e0Smrg    char *name;
67659607e0Smrg    MouseProtocolID val;
68659607e0Smrg} symtab_t;
69659607e0Smrg
70659607e0Smrg/* PnP EISA/product IDs */
71659607e0Smrgstatic symtab_t pnpprod[] = {
72659607e0Smrg    { "KML0001",  PROT_THINKING },	/* Kensignton ThinkingMouse */
73659607e0Smrg    { "MSH0001",  PROT_IMSERIAL },	/* MS IntelliMouse */
74659607e0Smrg    { "MSH0004",  PROT_IMSERIAL },	/* MS IntelliMouse TrackBall */
75659607e0Smrg    { "KYEEZ00",  PROT_MS },		/* Genius EZScroll */
76659607e0Smrg    { "KYE0001",  PROT_MS },		/* Genius PnP Mouse */
77659607e0Smrg    { "KYE0002",  PROT_MS },		/* MouseSystem (Genius?) SmartScroll */
78659607e0Smrg    { "KYE0003",  PROT_IMSERIAL },	/* Genius NetMouse */
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 */
105659607e0Smrg    { "PNP0F0C",  PROT_MS },		/* MS serial comatible */
106659607e0Smrg    { "PNP0F0D",  PROT_BM },		/* MS InPort comatible */
107659607e0Smrg    { "PNP0F0E",  PROT_PS2 },		/* MS PS/2 comatible */
108659607e0Smrg    { "PNP0F0F",  PROT_MS },		/* MS BallPoint comatible */
109659607e0Smrg#ifdef notyet
110659607e0Smrg    { "PNP0F10",  PROT_??? },		/* TI QuickPort */
111659607e0Smrg#endif
112659607e0Smrg    { "PNP0F11",  PROT_BM },		/* MS bus comatible */
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#if 1
192659607e0Smrg    last = mPriv->pnpLast;
193659607e0Smrg    mPriv->pnpLast = currentTime.milliseconds;
194659607e0Smrg
195659607e0Smrg    if (last) {
196659607e0Smrg	if (last - currentTime.milliseconds < 100
197659607e0Smrg	    || (mPriv->disablePnPauto
198659607e0Smrg		&& (last - currentTime.milliseconds < 10000))) {
199659607e0Smrg#ifdef EXTMOUSEDEBUG
200659607e0Smrg	    xf86ErrorF("Mouse: Disabling PnP\n");
201659607e0Smrg#endif
202659607e0Smrg	    mPriv->disablePnPauto = TRUE;
203659607e0Smrg	    return PROT_UNKNOWN;
204659607e0Smrg	}
205659607e0Smrg    }
206659607e0Smrg
207659607e0Smrg#ifdef EXTMOUSEDEBUG
208659607e0Smrg    if (mPriv->disablePnPauto)
209659607e0Smrg	xf86ErrorF("Mouse: Enabling PnP\n");
210659607e0Smrg#endif
211659607e0Smrg    mPriv->disablePnPauto = FALSE;
212659607e0Smrg
213659607e0Smrg    if (mPriv->soft)
214659607e0Smrg	return getPs2ProtocolPnP(pInfo);
215659607e0Smrg    else
216659607e0Smrg	return probePs2ProtocolPnP(pInfo);
217659607e0Smrg#else
218659607e0Smrg    return PROT_UNKNOWN;
219659607e0Smrg#endif
220659607e0Smrg}
221659607e0Smrg
222659607e0Smrg/*
223659607e0Smrg * Try to elicit a PnP ID as described in
224659607e0Smrg * Microsoft, Hayes: "Plug and Play External COM Device Specification,
225659607e0Smrg * rev 1.00", 1995.
226659607e0Smrg *
227659607e0Smrg * The routine does not fully implement the COM Enumerator as per Section
228659607e0Smrg * 2.1 of the document.  In particular, we don't have idle state in which
229659607e0Smrg * the driver software monitors the com port for dynamic connection or
230659607e0Smrg * removal of a device at the port, because `moused' simply quits if no
231659607e0Smrg * device is found.
232659607e0Smrg *
233659607e0Smrg * In addition, as PnP COM device enumeration procedure slightly has
234659607e0Smrg * changed since its first publication, devices which follow earlier
235659607e0Smrg * revisions of the above spec. may fail to respond if the rev 1.0
236659607e0Smrg * procedure is used. XXX
237659607e0Smrg */
238659607e0Smrgstatic int
239659607e0Smrgpnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP)
240659607e0Smrg{
241659607e0Smrg    int i;
242659607e0Smrg    char c;
243659607e0Smrg    pointer pnpOpts;
244659607e0Smrg
245659607e0Smrg#if 0
246659607e0Smrg    /*
247659607e0Smrg     * This is the procedure described in rev 1.0 of PnP COM device spec.
248659607e0Smrg     * Unfortunately, some devices which comform to earlier revisions of
249659607e0Smrg     * the spec gets confused and do not return the ID string...
250659607e0Smrg     */
251659607e0Smrg
252659607e0Smrg    /* port initialization (2.1.2) */
253659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
254659607e0Smrg	return 0;
255659607e0Smrg    i |= XF86_M_DTR;		/* DTR = 1 */
256659607e0Smrg    i &= ~XF86_M_RTS;		/* RTS = 0 */
257659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
258659607e0Smrg	goto disconnect_idle;
259659607e0Smrg    usleep(200000);
260659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
261659607e0Smrg	(i & XF86_M_DSR) == 0)
262659607e0Smrg	goto disconnect_idle;
263659607e0Smrg
264659607e0Smrg    /* port setup, 1st phase (2.1.3) */
265659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
266659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
267659607e0Smrg    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
268659607e0Smrg    xf86SerialModemClearBits(pInfo->fd, i);
269659607e0Smrg    usleep(200000);
270659607e0Smrg    i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
271659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
272659607e0Smrg    usleep(200000);
273659607e0Smrg
274659607e0Smrg    /* wait for response, 1st phase (2.1.4) */
275659607e0Smrg    xf86FlushInput(pInfo->fd);
276659607e0Smrg    i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
277659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
278659607e0Smrg
279659607e0Smrg    /* try to read something */
280659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0) {
281659607e0Smrg
282659607e0Smrg	/* port setup, 2nd phase (2.1.5) */
283659607e0Smrg        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
284659607e0Smrg	xf86SerialModemClearBits(pInfo->fd, i);
285659607e0Smrg        usleep(200000);
286659607e0Smrg
287659607e0Smrg	/* wait for respose, 2nd phase (2.1.6) */
288659607e0Smrg	xf86FlushInput(pInfo->fd);
289659607e0Smrg        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
290659607e0Smrg	xf86SerialModemSetBits(pInfo->fd, i);
291659607e0Smrg
292659607e0Smrg        /* try to read something */
293659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
294659607e0Smrg	    goto connect_idle;
295659607e0Smrg    }
296659607e0Smrg#else
297659607e0Smrg    /*
298659607e0Smrg     * This is a simplified procedure; it simply toggles RTS.
299659607e0Smrg     */
300659607e0Smrg
301659607e0Smrg    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
302659607e0Smrg	return 0;
303659607e0Smrg    i |= XF86_M_DTR;		/* DTR = 1 */
304659607e0Smrg    i &= ~XF86_M_RTS;		/* RTS = 0 */
305659607e0Smrg    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
306659607e0Smrg	goto disconnect_idle;
307659607e0Smrg    usleep(200000);
308659607e0Smrg
309659607e0Smrg    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
310659607e0Smrg    xf86SetSerial(pInfo->fd, pnpOpts);
311659607e0Smrg
312659607e0Smrg    /* wait for respose */
313659607e0Smrg    xf86FlushInput(pInfo->fd);
314659607e0Smrg    i = XF86_M_DTR | XF86_M_RTS;	/* DTR = 1, RTS = 1 */
315659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
316659607e0Smrg
317659607e0Smrg    /* try to read something */
318659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
319659607e0Smrg        goto connect_idle;
320659607e0Smrg#endif
321659607e0Smrg
322659607e0Smrg    /* collect PnP COM device ID (2.1.7) */
323659607e0Smrg    i = 0;
324659607e0Smrg    *prePNP = FALSE;
325659607e0Smrg
326659607e0Smrg    usleep(200000);	/* the mouse must send `Begin ID' within 200msec */
327659607e0Smrg    while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
328659607e0Smrg	/* we may see "M", or "M3..." before `Begin ID' */
329659607e0Smrg	if (c == 'M')
330659607e0Smrg	    *prePNP = TRUE;
331659607e0Smrg
332659607e0Smrg        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
333659607e0Smrg	    *prePNP = FALSE;
334659607e0Smrg	    buf[0] = c;
335659607e0Smrg	    i = 1;
336659607e0Smrg	    break;
337659607e0Smrg        }
338659607e0Smrg	if (*prePNP)
339659607e0Smrg	    buf[i++] = c;
340659607e0Smrg
341659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
342659607e0Smrg	    break;
343659607e0Smrg    }
344659607e0Smrg    if (i <= 0) {
345659607e0Smrg	/* we haven't seen `Begin ID' in time... */
346659607e0Smrg	goto connect_idle;
347659607e0Smrg    }
348659607e0Smrg    if (*prePNP)
349659607e0Smrg	return i;
350659607e0Smrg
351659607e0Smrg    ++c;			/* make it `End ID' */
352659607e0Smrg    for (;;) {
353659607e0Smrg	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
354659607e0Smrg	    break;
355659607e0Smrg
356659607e0Smrg	xf86ReadSerial(pInfo->fd, &buf[i], 1);
357659607e0Smrg        if (buf[i++] == c)	/* End ID */
358659607e0Smrg	    break;
359659607e0Smrg	if (i >= 256)
360659607e0Smrg	    break;
361659607e0Smrg    }
362659607e0Smrg    if (buf[i - 1] != c)
363659607e0Smrg	goto connect_idle;
364659607e0Smrg    return i;
365659607e0Smrg
366659607e0Smrg    /*
367659607e0Smrg     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
368659607e0Smrg     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
369659607e0Smrg     * assuming there is something at the port even if it didn't
370659607e0Smrg     * respond to the PnP enumeration procedure.
371659607e0Smrg     */
372659607e0Smrgdisconnect_idle:
373659607e0Smrg    i = XF86_M_DTR | XF86_M_RTS;		/* DTR = 1, RTS = 1 */
374659607e0Smrg    xf86SerialModemSetBits(pInfo->fd, i);
375659607e0Smrgconnect_idle:
376659607e0Smrg    return 0;
377659607e0Smrg}
378659607e0Smrg
379659607e0Smrgstatic int
380659607e0Smrgpnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len)
381659607e0Smrg{
382659607e0Smrg    char s[3];
383659607e0Smrg    int offset;
384659607e0Smrg    int sum = 0;
385659607e0Smrg    int i, j;
386659607e0Smrg
387659607e0Smrg    id->revision = 0;
388659607e0Smrg    id->eisaid = NULL;
389659607e0Smrg    id->serial = NULL;
390659607e0Smrg    id->class = NULL;
391659607e0Smrg    id->compat = NULL;
392659607e0Smrg    id->description = NULL;
393659607e0Smrg    id->neisaid = 0;
394659607e0Smrg    id->nserial = 0;
395659607e0Smrg    id->nclass = 0;
396659607e0Smrg    id->ncompat = 0;
397659607e0Smrg    id->ndescription = 0;
398659607e0Smrg
399659607e0Smrg    offset = 0x28 - buf[0];
400659607e0Smrg
401659607e0Smrg    /* calculate checksum */
402659607e0Smrg    for (i = 0; i < len - 3; ++i) {
403659607e0Smrg	sum += buf[i];
404659607e0Smrg	buf[i] += offset;
405659607e0Smrg    }
406659607e0Smrg    sum += buf[len - 1];
407659607e0Smrg    for (; i < len; ++i)
408659607e0Smrg	buf[i] += offset;
409659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
410659607e0Smrg		len, len, buf);
411659607e0Smrg
412659607e0Smrg    /* revision */
413659607e0Smrg    buf[1] -= offset;
414659607e0Smrg    buf[2] -= offset;
415659607e0Smrg    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
416659607e0Smrg    xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name,
417659607e0Smrg		id->revision / 100, id->revision % 100);
418659607e0Smrg
419659607e0Smrg    /* EISA vender and product ID */
420659607e0Smrg    id->eisaid = &buf[3];
421659607e0Smrg    id->neisaid = 7;
422659607e0Smrg
423659607e0Smrg    /* option strings */
424659607e0Smrg    i = 10;
425659607e0Smrg    if (buf[i] == '\\') {
426659607e0Smrg        /* device serial # */
427659607e0Smrg        for (j = ++i; i < len; ++i) {
428659607e0Smrg            if (buf[i] == '\\')
429659607e0Smrg		break;
430659607e0Smrg        }
431659607e0Smrg	if (i >= len)
432659607e0Smrg	    i -= 3;
433659607e0Smrg	if (i - j == 8) {
434659607e0Smrg            id->serial = &buf[j];
435659607e0Smrg            id->nserial = 8;
436659607e0Smrg	}
437659607e0Smrg    }
438659607e0Smrg    if (buf[i] == '\\') {
439659607e0Smrg        /* PnP class */
440659607e0Smrg        for (j = ++i; i < len; ++i) {
441659607e0Smrg            if (buf[i] == '\\')
442659607e0Smrg		break;
443659607e0Smrg        }
444659607e0Smrg	if (i >= len)
445659607e0Smrg	    i -= 3;
446659607e0Smrg	if (i > j + 1) {
447659607e0Smrg            id->class = &buf[j];
448659607e0Smrg            id->nclass = i - j;
449659607e0Smrg        }
450659607e0Smrg    }
451659607e0Smrg    if (buf[i] == '\\') {
452659607e0Smrg	/* compatible driver */
453659607e0Smrg        for (j = ++i; i < len; ++i) {
454659607e0Smrg            if (buf[i] == '\\')
455659607e0Smrg		break;
456659607e0Smrg        }
457659607e0Smrg	/*
458659607e0Smrg	 * PnP COM spec prior to v0.96 allowed '*' in this field,
459659607e0Smrg	 * it's not allowed now; just ignore it.
460659607e0Smrg	 */
461659607e0Smrg	if (buf[j] == '*')
462659607e0Smrg	    ++j;
463659607e0Smrg	if (i >= len)
464659607e0Smrg	    i -= 3;
465659607e0Smrg	if (i > j + 1) {
466659607e0Smrg            id->compat = &buf[j];
467659607e0Smrg            id->ncompat = i - j;
468659607e0Smrg        }
469659607e0Smrg    }
470659607e0Smrg    if (buf[i] == '\\') {
471659607e0Smrg	/* product description */
472659607e0Smrg        for (j = ++i; i < len; ++i) {
473659607e0Smrg            if (buf[i] == ';')
474659607e0Smrg		break;
475659607e0Smrg        }
476659607e0Smrg	if (i >= len)
477659607e0Smrg	    i -= 3;
478659607e0Smrg	if (i > j + 1) {
479659607e0Smrg            id->description = &buf[j];
480659607e0Smrg            id->ndescription = i - j;
481659607e0Smrg        }
482659607e0Smrg    }
483659607e0Smrg
484659607e0Smrg    /* checksum exists if there are any optional fields */
485659607e0Smrg    if ((id->nserial > 0) || (id->nclass > 0)
486659607e0Smrg	|| (id->ncompat > 0) || (id->ndescription > 0)) {
487659607e0Smrg	xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum);
488659607e0Smrg        sprintf(s, "%02X", sum & 0x0ff);
489659607e0Smrg        if (strncmp(s, &buf[len - 3], 2) != 0) {
490659607e0Smrg#if 0
491659607e0Smrg            /*
492659607e0Smrg	     * Checksum error!!
493659607e0Smrg	     * I found some mice do not comply with the PnP COM device
494659607e0Smrg	     * spec regarding checksum... XXX
495659607e0Smrg	     */
496659607e0Smrg	    return FALSE;
497659607e0Smrg#endif
498659607e0Smrg        }
499659607e0Smrg    }
500659607e0Smrg
501659607e0Smrg    return TRUE;
502659607e0Smrg}
503659607e0Smrg
504659607e0Smrg/* We can only identify MS at the moment */
505659607e0Smrgstatic MouseProtocolID
506659607e0Smrgprepnpparse(InputInfoPtr pInfo, char *buf)
507659607e0Smrg{
508659607e0Smrg    if (buf[0] == 'M' && buf[1] == '3')
509659607e0Smrg	return PROT_MS;
510659607e0Smrg    return PROT_UNKNOWN;
511659607e0Smrg}
512659607e0Smrg
513659607e0Smrg
514659607e0Smrgstatic symtab_t *
515659607e0Smrgpnpproto(pnpid_t *id)
516659607e0Smrg{
517659607e0Smrg    symtab_t *t;
518659607e0Smrg    int i, j;
519659607e0Smrg
520659607e0Smrg    if (id->nclass > 0)
521659607e0Smrg	if (strncmp(id->class, "MOUSE", id->nclass) != 0)
522659607e0Smrg	    /* this is not a mouse! */
523659607e0Smrg	    return NULL;
524659607e0Smrg
525659607e0Smrg    if (id->neisaid > 0) {
526659607e0Smrg        t = gettoken(pnpprod, id->eisaid, id->neisaid);
527659607e0Smrg	if (t->val != -1)
528659607e0Smrg            return t;
529659607e0Smrg    }
530659607e0Smrg
531659607e0Smrg    /*
532659607e0Smrg     * The 'Compatible drivers' field may contain more than one
533659607e0Smrg     * ID separated by ','.
534659607e0Smrg     */
535659607e0Smrg    if (id->ncompat <= 0)
536659607e0Smrg	return NULL;
537659607e0Smrg    for (i = 0; i < id->ncompat; ++i) {
538659607e0Smrg        for (j = i; id->compat[i] != ','; ++i)
539659607e0Smrg            if (i >= id->ncompat)
540659607e0Smrg		break;
541659607e0Smrg        if (i > j) {
542659607e0Smrg            t = gettoken(pnpprod, id->compat + j, i - j);
543659607e0Smrg	    if (t->val != -1)
544659607e0Smrg                return t;
545659607e0Smrg	}
546659607e0Smrg    }
547659607e0Smrg
548659607e0Smrg    return NULL;
549659607e0Smrg}
550659607e0Smrg
551659607e0Smrg/* name/val mapping */
552659607e0Smrg
553659607e0Smrgstatic symtab_t *
554659607e0Smrggettoken(tab, s, len)
555659607e0Smrgsymtab_t *tab;
556659607e0Smrgchar *s;
557659607e0Smrgint len;
558659607e0Smrg{
559659607e0Smrg    int i;
560659607e0Smrg
561659607e0Smrg    for (i = 0; tab[i].name != NULL; ++i) {
562659607e0Smrg	if (strncmp(tab[i].name, s, len) == 0)
563659607e0Smrg	    break;
564659607e0Smrg    }
565659607e0Smrg    return &tab[i];
566659607e0Smrg}
567659607e0Smrg
568659607e0Smrg/******************* PS/2 PnP probing ****************/
569659607e0Smrg
570659607e0Smrgstatic int
571659607e0SmrgreadMouse(InputInfoPtr pInfo, unsigned char *u)
572659607e0Smrg{
573659607e0Smrg
574659607e0Smrg    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
575659607e0Smrg	return FALSE;
576659607e0Smrg
577659607e0Smrg    xf86ReadSerial(pInfo->fd, u, 1);
578659607e0Smrg    return TRUE;
579659607e0Smrg}
580659607e0Smrg
581659607e0Smrgstatic void
582659607e0Smrgps2DisableWrapMode(InputInfoPtr pInfo)
583659607e0Smrg{
584659607e0Smrg    unsigned char reset_wrap_mode[] = { 0xEC };
585659607e0Smrg    ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
586659607e0Smrg}
587659607e0Smrg
588659607e0SmrgBool
589659607e0Smrgps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len)
590659607e0Smrg{
591659607e0Smrg    unsigned char c;
592659607e0Smrg    int i,j;
593659607e0Smrg
594659607e0Smrg#ifdef DEBUG
595659607e0Smrg    xf86ErrorF("Ps/2 data package:");
596659607e0Smrg    for (i = 0; i < len; i++)
597659607e0Smrg	xf86ErrorF(" %x", *(bytes + i));
598659607e0Smrg    xf86ErrorF("\n");
599659607e0Smrg#endif
600659607e0Smrg
601659607e0Smrg    for (i = 0; i < len; i++) {
602659607e0Smrg	for (j = 0; j < 10; j++) {
603659607e0Smrg	    xf86WriteSerial(pInfo->fd, bytes + i, 1);
604659607e0Smrg	    usleep(10000);
605659607e0Smrg	    if (!readMouse(pInfo,&c)) {
606659607e0Smrg#ifdef DEBUG
607659607e0Smrg		xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i));
608659607e0Smrg#endif
609659607e0Smrg		return FALSE;
610659607e0Smrg	    }
611659607e0Smrg#ifdef DEBUG
612659607e0Smrg	    xf86ErrorF("Recieved: 0x%x\n",c);
613659607e0Smrg#endif
614659607e0Smrg	    if (c == 0xFA) /* ACK */
615659607e0Smrg		break;
616659607e0Smrg
617659607e0Smrg	    if (c == 0xFE) /* resend */
618659607e0Smrg		continue;
619659607e0Smrg
620659607e0Smrg
621659607e0Smrg	    if (c == 0xFC) /* error */
622659607e0Smrg		return FALSE;
623659607e0Smrg
624659607e0Smrg	    /* Some mice accidently enter wrap mode during init */
625659607e0Smrg	    if (c == *(bytes + i)    /* wrap mode */
626659607e0Smrg		&& (*(bytes + i) != 0xEC)) /* avoid recursion */
627659607e0Smrg		ps2DisableWrapMode(pInfo);
628659607e0Smrg
629659607e0Smrg	    return FALSE;
630659607e0Smrg	}
631659607e0Smrg	if (j == 10)
632659607e0Smrg	    return FALSE;
633659607e0Smrg    }
634659607e0Smrg
635659607e0Smrg    return TRUE;
636659607e0Smrg}
637659607e0Smrg
638659607e0Smrgstatic Bool
639659607e0Smrgps2DisableDataReporting(InputInfoPtr pInfo)
640659607e0Smrg{
641659607e0Smrg    unsigned char packet[] = { 0xF5 };
642659607e0Smrg    return ps2SendPacket(pInfo, packet, sizeof(packet));
643659607e0Smrg}
644659607e0Smrg
645659607e0SmrgBool
646659607e0Smrgps2EnableDataReporting(InputInfoPtr pInfo)
647659607e0Smrg{
648659607e0Smrg    unsigned char packet[] = { 0xF4 };
649659607e0Smrg    return ps2SendPacket(pInfo, packet, sizeof(packet));
650659607e0Smrg}
651659607e0Smrg
652659607e0Smrgint
653659607e0Smrgps2GetDeviceID(InputInfoPtr pInfo)
654659607e0Smrg{
655659607e0Smrg    unsigned char u;
656659607e0Smrg    unsigned char packet[] = { 0xf2 };
657659607e0Smrg
658659607e0Smrg    usleep(30000);
659659607e0Smrg    xf86FlushInput(pInfo->fd);
660659607e0Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
661659607e0Smrg	return -1;
662659607e0Smrg    while (1) {
663659607e0Smrg	if (!readMouse(pInfo,&u))
664659607e0Smrg	    return -1;
665659607e0Smrg	if (u != 0xFA)
666659607e0Smrg	    break;
667659607e0Smrg    }
668659607e0Smrg#ifdef DEBUG
669659607e0Smrg    xf86ErrorF("Obtained Mouse Type: %x\n",u);
670659607e0Smrg#endif
671659607e0Smrg    return (int) u;
672659607e0Smrg}
673659607e0Smrg
674659607e0SmrgBool
675659607e0Smrgps2Reset(InputInfoPtr pInfo)
676659607e0Smrg{
677659607e0Smrg    unsigned char u;
678659607e0Smrg    unsigned char packet[] = { 0xff };
679659607e0Smrg    unsigned char reply[] = { 0xaa, 0x00 };
680659607e0Smrg    unsigned int i;
681659607e0Smrg#ifdef DEBUG
682659607e0Smrg   xf86ErrorF("PS/2 Mouse reset\n");
683659607e0Smrg#endif
684659607e0Smrg    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
685659607e0Smrg	return FALSE;
686659607e0Smrg    /* we need a little delay here */
687659607e0Smrg    xf86WaitForInput(pInfo->fd, 500000);
688659607e0Smrg    for (i = 0; i < sizeof(reply) ; i++) {
689659607e0Smrg	if (!readMouse(pInfo,&u)) {
690659607e0Smrg	    goto EXIT;
691659607e0Smrg	}
692659607e0Smrg	if (u != reply[i])
693659607e0Smrg	    goto EXIT;
694659607e0Smrg    }
695659607e0Smrg    return TRUE;
696659607e0Smrg
697659607e0Smrg EXIT:
698659607e0Smrg    xf86FlushInput(pInfo->fd);
699659607e0Smrg    return FALSE;
700659607e0Smrg}
701659607e0Smrg
702659607e0Smrgstatic MouseProtocolID
703659607e0SmrgprobePs2ProtocolPnP(InputInfoPtr pInfo)
704659607e0Smrg{
705659607e0Smrg    unsigned char u;
706659607e0Smrg    MouseProtocolID ret = PROT_UNKNOWN;
707659607e0Smrg
708659607e0Smrg    xf86FlushInput(pInfo->fd);
709659607e0Smrg
710659607e0Smrg    ps2DisableDataReporting(pInfo);
711659607e0Smrg
712659607e0Smrg    if (ps2Reset(pInfo)) { /* Reset PS2 device */
713659607e0Smrg  	unsigned char seq[] = { 243, 200, 243, 100, 243, 80 };
714659607e0Smrg	/* Try to identify Intelli Mouse */
715659607e0Smrg	if (ps2SendPacket(pInfo, seq, sizeof(seq))) {
716659607e0Smrg	    u = ps2GetDeviceID(pInfo);
717659607e0Smrg	    if (u == 0x03) {
718659607e0Smrg		/* found IntelliMouse now try IntelliExplorer */
719659607e0Smrg		unsigned char seq[] = { 243, 200, 243, 200, 243, 80 };
720659607e0Smrg		if (ps2SendPacket(pInfo,seq,sizeof(seq))) {
721659607e0Smrg		    u = ps2GetDeviceID(pInfo);
722659607e0Smrg		    if (u == 0x04)
723659607e0Smrg			ret =  PROT_EXPPS2;
724659607e0Smrg		    else
725659607e0Smrg			ret = PROT_IMPS2;
726659607e0Smrg		}
727659607e0Smrg	    } else if (ps2Reset(pInfo))  /* reset again to find sane state */
728659607e0Smrg		ret = PROT_PS2;
729659607e0Smrg	}
730659607e0Smrg
731659607e0Smrg	if (ret != PROT_UNKNOWN)
732659607e0Smrg	    ps2EnableDataReporting(pInfo);
733659607e0Smrg    }
734659607e0Smrg    return ret;
735659607e0Smrg}
736659607e0Smrg
737659607e0Smrgstatic struct ps2protos {
738659607e0Smrg    int Id;
739659607e0Smrg    MouseProtocolID protoID;
740659607e0Smrg} ps2 [] = {
741659607e0Smrg    { 0x0, PROT_PS2 },
742659607e0Smrg    { 0x3, PROT_IMPS2 },
743659607e0Smrg    { 0x4, PROT_EXPPS2 },
744659607e0Smrg    { -1 , PROT_UNKNOWN }
745659607e0Smrg};
746659607e0Smrg
747659607e0Smrg
748659607e0Smrgstatic MouseProtocolID
749659607e0SmrggetPs2ProtocolPnP(InputInfoPtr pInfo)
750659607e0Smrg{
751659607e0Smrg    int Id;
752659607e0Smrg    int i;
753659607e0Smrg    MouseProtocolID proto;
754659607e0Smrg    int count = 4;
755659607e0Smrg
756659607e0Smrg    xf86FlushInput(pInfo->fd);
757659607e0Smrg
758659607e0Smrg    while (--count)
759659607e0Smrg	if (ps2DisableDataReporting(pInfo))
760659607e0Smrg	    break;
761659607e0Smrg
762659607e0Smrg    if (!count) {
763659607e0Smrg	proto = PROT_UNKNOWN;
764659607e0Smrg	goto EXIT;
765659607e0Smrg    }
766659607e0Smrg
767659607e0Smrg    if ((Id = ps2GetDeviceID(pInfo)) == -1) {
768659607e0Smrg	proto = PROT_UNKNOWN;
769659607e0Smrg	goto EXIT;
770659607e0Smrg    }
771659607e0Smrg
772659607e0Smrg    if (-1 == ps2EnableDataReporting(pInfo)) {
773659607e0Smrg	proto = PROT_UNKNOWN;
774659607e0Smrg	goto EXIT;
775659607e0Smrg    }
776659607e0Smrg
777659607e0Smrg    for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) {
778659607e0Smrg	if (ps2[i].Id == Id) {
779659607e0Smrg	    xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id);
780659607e0Smrg	    proto =  ps2[i].protoID;
781659607e0Smrg	    goto EXIT;
782659607e0Smrg	}
783659607e0Smrg    }
784659607e0Smrg
785659607e0Smrg    proto = PROT_UNKNOWN;
786659607e0Smrg    xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id);
787659607e0Smrg
788659607e0Smrg EXIT:
789659607e0Smrg    xf86FlushInput(pInfo->fd);
790659607e0Smrg    return proto;
791659607e0Smrg}
792659607e0Smrg
793