pnp.c revision 6aab59a7
1/*
2 * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Kazutaka YOKOTA not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Kazutaka YOKOTA makes no representations
11 * about the suitability of this software for any purpose.  It is provided
12 * "as is" without express or implied warranty.
13 *
14 * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <stdio.h>
28#include <string.h>
29#include <unistd.h>
30#define NEED_EVENTS
31#include <X11/X.h>
32#include <X11/Xproto.h>
33#include "inputstr.h"
34#include "scrnintstr.h"
35#include "xf86.h"
36#include "xf86Priv.h"
37#include "xf86Xinput.h"
38#include "xf86_OSproc.h"
39#include "xf86OSmouse.h"
40#include "mouse.h"
41#include "mousePriv.h"
42
43#ifdef MOUSEINITDEBUG
44# define DEBUG
45# define EXTMOUSEDEBUG
46#endif
47
48/* serial PnP ID string */
49typedef struct {
50    int revision;	/* PnP revision, 100 for 1.00 */
51    char *eisaid;	/* EISA ID including mfr ID and product ID */
52    char *serial;	/* serial No, optional */
53    char *class;	/* device class, optional */
54    char *compat;	/* list of compatible drivers, optional */
55    char *description;	/* product description, optional */
56    int neisaid;	/* length of the above fields... */
57    int nserial;
58    int nclass;
59    int ncompat;
60    int ndescription;
61} pnpid_t;
62
63/* symbol table entry */
64typedef struct {
65    char *name;
66    MouseProtocolID val;
67} symtab_t;
68
69/* PnP EISA/product IDs */
70static symtab_t pnpprod[] = {
71    { "KML0001",  PROT_THINKING },	/* Kensington ThinkingMouse */
72    { "MSH0001",  PROT_IMSERIAL },	/* MS IntelliMouse */
73    { "MSH0004",  PROT_IMSERIAL },	/* MS IntelliMouse TrackBall */
74    { "KYEEZ00",  PROT_MS },		/* Genius EZScroll */
75    { "KYE0001",  PROT_MS },		/* Genius PnP Mouse */
76    { "KYE0002",  PROT_MS },		/* MouseSystem (Genius?) SmartScroll */
77    { "KYE0003",  PROT_IMSERIAL },	/* Genius NetMouse */
78    { "LGI800C",  PROT_IMSERIAL },	/* Logitech MouseMan (4 button model) */
79    { "LGI8033",  PROT_IMSERIAL },	/* Logitech Cordless MouseMan Wheel */
80    { "LGI8050",  PROT_IMSERIAL },	/* Logitech MouseMan+ */
81    { "LGI8051",  PROT_IMSERIAL },	/* Logitech FirstMouse+ */
82    { "LGI8001",  PROT_LOGIMAN },	/* Logitech serial */
83    { "A4W0005",  PROT_IMSERIAL },	/* A4 Tech 4D/4D+ Mouse */
84    { "PEC9802",  PROT_IMSERIAL },	/* 8D Scroll Mouse */
85
86    { "PNP0F00",  PROT_BM },		/* MS bus */
87    { "PNP0F01",  PROT_MS },		/* MS serial */
88    { "PNP0F02",  PROT_BM },		/* MS InPort */
89    { "PNP0F03",  PROT_PS2 },		/* MS PS/2 */
90    /*
91     * EzScroll returns PNP0F04 in the compatible device field; but it
92     * doesn't look compatible... XXX
93     */
94    { "PNP0F04",  PROT_MSC },		/* MouseSystems */
95    { "PNP0F05",  PROT_MSC },		/* MouseSystems */
96#ifdef notyet
97    { "PNP0F06",  PROT_??? },		/* Genius Mouse */
98    { "PNP0F07",  PROT_??? },		/* Genius Mouse */
99#endif
100    { "PNP0F08",  PROT_LOGIMAN },	/* Logitech serial */
101    { "PNP0F09",  PROT_MS },		/* MS BallPoint serial */
102    { "PNP0F0A",  PROT_MS },		/* MS PnP serial */
103    { "PNP0F0B",  PROT_MS },		/* MS PnP BallPoint serial */
104    { "PNP0F0C",  PROT_MS },		/* MS serial compatible */
105    { "PNP0F0D",  PROT_BM },		/* MS InPort compatible */
106    { "PNP0F0E",  PROT_PS2 },		/* MS PS/2 compatible */
107    { "PNP0F0F",  PROT_MS },		/* MS BallPoint compatible */
108#ifdef notyet
109    { "PNP0F10",  PROT_??? },		/* TI QuickPort */
110#endif
111    { "PNP0F11",  PROT_BM },		/* MS bus compatible */
112    { "PNP0F12",  PROT_PS2 },		/* Logitech PS/2 */
113    { "PNP0F13",  PROT_PS2 },		/* PS/2 */
114#ifdef notyet
115    { "PNP0F14",  PROT_??? },		/* MS Kids Mouse */
116#endif
117    { "PNP0F15",  PROT_BM },		/* Logitech bus */
118#ifdef notyet
119    { "PNP0F16",  PROT_??? },		/* Logitech SWIFT */
120#endif
121    { "PNP0F17",  PROT_LOGIMAN },	/* Logitech serial compat */
122    { "PNP0F18",  PROT_BM },		/* Logitech bus compatible */
123    { "PNP0F19",  PROT_PS2 },		/* Logitech PS/2 compatible */
124#ifdef notyet
125    { "PNP0F1A",  PROT_??? },		/* Logitech SWIFT compatible */
126    { "PNP0F1B",  PROT_??? },		/* HP Omnibook */
127    { "PNP0F1C",  PROT_??? },		/* Compaq LTE TrackBall PS/2 */
128    { "PNP0F1D",  PROT_??? },		/* Compaq LTE TrackBall serial */
129    { "PNP0F1E",  PROT_??? },		/* MS Kids Trackball */
130#endif
131    { NULL,	  PROT_UNKNOWN },
132};
133
134static const char *pnpSerial[] = {
135	"BaudRate",	"1200",
136	"DataBits",	"7",
137	"StopBits",	"1",
138	"Parity",	"None",
139	"FlowControl",	"None",
140	"VTime",	"0",
141	"VMin",		"1",
142	NULL
143};
144
145static int pnpgets(InputInfoPtr, char *, Bool *prePNP);
146static int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
147static MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf);
148static symtab_t *pnpproto(pnpid_t *);
149static symtab_t *gettoken(symtab_t *, char *, int);
150static MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo);
151static MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo);
152
153static MouseProtocolID
154MouseGetSerialPnpProtocol(InputInfoPtr pInfo)
155{
156    char buf[256];	/* PnP ID string may be up to 256 bytes long */
157    pnpid_t pnpid;
158    symtab_t *t;
159    int len;
160    Bool prePNP;
161
162    if ((len = pnpgets(pInfo, buf, &prePNP)) > 0)
163    {
164	if (!prePNP) {
165	    if (pnpparse(pInfo, &pnpid, buf, len) &&
166		(t = pnpproto(&pnpid)) != NULL) {
167		xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n",
168			    pInfo->name, t->val);
169		return (t->val);
170	    }
171	} else
172	    return prepnpparse(pInfo,buf);
173    }
174    return PROT_UNKNOWN;
175}
176
177MouseProtocolID
178MouseGetPnpProtocol(InputInfoPtr pInfo)
179{
180    MouseDevPtr  pMse = pInfo->private;
181    mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv;
182    MouseProtocolID val;
183    CARD32 last;
184
185    if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) {
186	if (val == MouseGetSerialPnpProtocol(pInfo))
187	    return val;
188    }
189
190    last = mPriv->pnpLast;
191    mPriv->pnpLast = currentTime.milliseconds;
192
193    if (last) {
194	if (last - currentTime.milliseconds < 100
195	    || (mPriv->disablePnPauto
196		&& (last - currentTime.milliseconds < 10000))) {
197#ifdef EXTMOUSEDEBUG
198	    xf86ErrorF("Mouse: Disabling PnP\n");
199#endif
200	    mPriv->disablePnPauto = TRUE;
201	    return PROT_UNKNOWN;
202	}
203    }
204
205#ifdef EXTMOUSEDEBUG
206    if (mPriv->disablePnPauto)
207	xf86ErrorF("Mouse: Enabling PnP\n");
208#endif
209    mPriv->disablePnPauto = FALSE;
210
211    if (mPriv->soft)
212	return getPs2ProtocolPnP(pInfo);
213    else
214	return probePs2ProtocolPnP(pInfo);
215}
216
217/*
218 * Try to elicit a PnP ID as described in
219 * Microsoft, Hayes: "Plug and Play External COM Device Specification,
220 * rev 1.00", 1995.
221 *
222 * The routine does not fully implement the COM Enumerator as per Section
223 * 2.1 of the document.  In particular, we don't have idle state in which
224 * the driver software monitors the com port for dynamic connection or
225 * removal of a device at the port, because `moused' simply quits if no
226 * device is found.
227 *
228 * In addition, as PnP COM device enumeration procedure slightly has
229 * changed since its first publication, devices which follow earlier
230 * revisions of the above spec. may fail to respond if the rev 1.0
231 * procedure is used. XXX
232 */
233static int
234pnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP)
235{
236    int i;
237    char c;
238    pointer pnpOpts;
239
240#if 0
241    /*
242     * This is the procedure described in rev 1.0 of PnP COM device spec.
243     * Unfortunately, some devices which conform to earlier revisions of
244     * the spec gets confused and do not return the ID string...
245     */
246
247    /* port initialization (2.1.2) */
248    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
249	return 0;
250    i |= XF86_M_DTR;		/* DTR = 1 */
251    i &= ~XF86_M_RTS;		/* RTS = 0 */
252    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
253	goto disconnect_idle;
254    usleep(200000);
255    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
256	(i & XF86_M_DSR) == 0)
257	goto disconnect_idle;
258
259    /* port setup, 1st phase (2.1.3) */
260    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
261    xf86SetSerial(pInfo->fd, pnpOpts);
262    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
263    xf86SerialModemClearBits(pInfo->fd, i);
264    usleep(200000);
265    i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
266    xf86SerialModemSetBits(pInfo->fd, i);
267    usleep(200000);
268
269    /* wait for response, 1st phase (2.1.4) */
270    xf86FlushInput(pInfo->fd);
271    i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
272    xf86SerialModemSetBits(pInfo->fd, i);
273
274    /* try to read something */
275    if (xf86WaitForInput(pInfo->fd, 200000) <= 0) {
276
277	/* port setup, 2nd phase (2.1.5) */
278        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
279	xf86SerialModemClearBits(pInfo->fd, i);
280        usleep(200000);
281
282	/* wait for response, 2nd phase (2.1.6) */
283	xf86FlushInput(pInfo->fd);
284        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
285	xf86SerialModemSetBits(pInfo->fd, i);
286
287        /* try to read something */
288	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
289	    goto connect_idle;
290    }
291#else
292    /*
293     * This is a simplified procedure; it simply toggles RTS.
294     */
295
296    if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
297	return 0;
298    i |= XF86_M_DTR;		/* DTR = 1 */
299    i &= ~XF86_M_RTS;		/* RTS = 0 */
300    if (xf86SetSerialModemState(pInfo->fd, i) == -1)
301	goto disconnect_idle;
302    usleep(200000);
303
304    pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
305    xf86SetSerial(pInfo->fd, pnpOpts);
306
307    /* wait for response */
308    xf86FlushInput(pInfo->fd);
309    i = XF86_M_DTR | XF86_M_RTS;	/* DTR = 1, RTS = 1 */
310    xf86SerialModemSetBits(pInfo->fd, i);
311
312    /* try to read something */
313    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
314        goto connect_idle;
315#endif
316
317    /* collect PnP COM device ID (2.1.7) */
318    i = 0;
319    *prePNP = FALSE;
320
321    usleep(200000);	/* the mouse must send `Begin ID' within 200msec */
322    while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
323	/* we may see "M", or "M3..." before `Begin ID' */
324	if (c == 'M')
325	    *prePNP = TRUE;
326
327        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
328	    *prePNP = FALSE;
329	    buf[0] = c;
330	    i = 1;
331	    break;
332        }
333	if (*prePNP)
334	    buf[i++] = c;
335
336	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
337	    break;
338    }
339    if (i <= 0) {
340	/* we haven't seen `Begin ID' in time... */
341	goto connect_idle;
342    }
343    if (*prePNP)
344	return i;
345
346    ++c;			/* make it `End ID' */
347    for (;;) {
348	if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
349	    break;
350
351	xf86ReadSerial(pInfo->fd, &buf[i], 1);
352        if (buf[i++] == c)	/* End ID */
353	    break;
354	if (i >= 256)
355	    break;
356    }
357    if (buf[i - 1] != c)
358	goto connect_idle;
359    return i;
360
361    /*
362     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
363     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
364     * assuming there is something at the port even if it didn't
365     * respond to the PnP enumeration procedure.
366     */
367disconnect_idle:
368    i = XF86_M_DTR | XF86_M_RTS;		/* DTR = 1, RTS = 1 */
369    xf86SerialModemSetBits(pInfo->fd, i);
370connect_idle:
371    return 0;
372}
373
374static int
375pnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len)
376{
377    char s[3];
378    int offset;
379    int sum = 0;
380    int i, j;
381
382    id->revision = 0;
383    id->eisaid = NULL;
384    id->serial = NULL;
385    id->class = NULL;
386    id->compat = NULL;
387    id->description = NULL;
388    id->neisaid = 0;
389    id->nserial = 0;
390    id->nclass = 0;
391    id->ncompat = 0;
392    id->ndescription = 0;
393
394    offset = 0x28 - buf[0];
395
396    /* calculate checksum */
397    for (i = 0; i < len - 3; ++i) {
398	sum += buf[i];
399	buf[i] += offset;
400    }
401    sum += buf[len - 1];
402    for (; i < len; ++i)
403	buf[i] += offset;
404    xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
405		len, len, buf);
406
407    /* revision */
408    buf[1] -= offset;
409    buf[2] -= offset;
410    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
411    xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name,
412		id->revision / 100, id->revision % 100);
413
414    /* EISA vender and product ID */
415    id->eisaid = &buf[3];
416    id->neisaid = 7;
417
418    /* option strings */
419    i = 10;
420    if (buf[i] == '\\') {
421        /* device serial # */
422        for (j = ++i; i < len; ++i) {
423            if (buf[i] == '\\')
424		break;
425        }
426	if (i >= len)
427	    i -= 3;
428	if (i - j == 8) {
429            id->serial = &buf[j];
430            id->nserial = 8;
431	}
432    }
433    if (buf[i] == '\\') {
434        /* PnP class */
435        for (j = ++i; i < len; ++i) {
436            if (buf[i] == '\\')
437		break;
438        }
439	if (i >= len)
440	    i -= 3;
441	if (i > j + 1) {
442            id->class = &buf[j];
443            id->nclass = i - j;
444        }
445    }
446    if (buf[i] == '\\') {
447	/* compatible driver */
448        for (j = ++i; i < len; ++i) {
449            if (buf[i] == '\\')
450		break;
451        }
452	/*
453	 * PnP COM spec prior to v0.96 allowed '*' in this field,
454	 * it's not allowed now; just ignore it.
455	 */
456	if (buf[j] == '*')
457	    ++j;
458	if (i >= len)
459	    i -= 3;
460	if (i > j + 1) {
461            id->compat = &buf[j];
462            id->ncompat = i - j;
463        }
464    }
465    if (buf[i] == '\\') {
466	/* product description */
467        for (j = ++i; i < len; ++i) {
468            if (buf[i] == ';')
469		break;
470        }
471	if (i >= len)
472	    i -= 3;
473	if (i > j + 1) {
474            id->description = &buf[j];
475            id->ndescription = i - j;
476        }
477    }
478
479    /* checksum exists if there are any optional fields */
480    if ((id->nserial > 0) || (id->nclass > 0)
481	|| (id->ncompat > 0) || (id->ndescription > 0)) {
482	xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum);
483        sprintf(s, "%02X", sum & 0x0ff);
484        if (strncmp(s, &buf[len - 3], 2) != 0) {
485#if 0
486            /*
487	     * Checksum error!!
488	     * I found some mice do not comply with the PnP COM device
489	     * spec regarding checksum... XXX
490	     */
491	    return FALSE;
492#endif
493        }
494    }
495
496    return TRUE;
497}
498
499/* We can only identify MS at the moment */
500static MouseProtocolID
501prepnpparse(InputInfoPtr pInfo, char *buf)
502{
503    if (buf[0] == 'M' && buf[1] == '3')
504	return PROT_MS;
505    return PROT_UNKNOWN;
506}
507
508
509static symtab_t *
510pnpproto(pnpid_t *id)
511{
512    symtab_t *t;
513    int i, j;
514
515    if (id->nclass > 0)
516	if (strncmp(id->class, "MOUSE", id->nclass) != 0)
517	    /* this is not a mouse! */
518	    return NULL;
519
520    if (id->neisaid > 0) {
521        t = gettoken(pnpprod, id->eisaid, id->neisaid);
522	if (t->val != -1)
523            return t;
524    }
525
526    /*
527     * The 'Compatible drivers' field may contain more than one
528     * ID separated by ','.
529     */
530    if (id->ncompat <= 0)
531	return NULL;
532    for (i = 0; i < id->ncompat; ++i) {
533        for (j = i; id->compat[i] != ','; ++i)
534            if (i >= id->ncompat)
535		break;
536        if (i > j) {
537            t = gettoken(pnpprod, id->compat + j, i - j);
538	    if (t->val != -1)
539                return t;
540	}
541    }
542
543    return NULL;
544}
545
546/* name/val mapping */
547
548static symtab_t *
549gettoken(symtab_t *tab, char *s, int len)
550{
551    int i;
552
553    for (i = 0; tab[i].name != NULL; ++i) {
554	if (strncmp(tab[i].name, s, len) == 0)
555	    break;
556    }
557    return &tab[i];
558}
559
560/******************* PS/2 PnP probing ****************/
561
562static int
563readMouse(InputInfoPtr pInfo, unsigned char *u)
564{
565
566    if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
567	return FALSE;
568
569    xf86ReadSerial(pInfo->fd, u, 1);
570    return TRUE;
571}
572
573static void
574ps2DisableWrapMode(InputInfoPtr pInfo)
575{
576    unsigned char reset_wrap_mode[] = { 0xEC };
577    ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
578}
579
580Bool
581ps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len)
582{
583    unsigned char c;
584    int i,j;
585
586#ifdef DEBUG
587    xf86ErrorF("Ps/2 data package:");
588    for (i = 0; i < len; i++)
589	xf86ErrorF(" %x", *(bytes + i));
590    xf86ErrorF("\n");
591#endif
592
593    for (i = 0; i < len; i++) {
594	for (j = 0; j < 10; j++) {
595	    xf86WriteSerial(pInfo->fd, bytes + i, 1);
596	    usleep(10000);
597	    if (!readMouse(pInfo,&c)) {
598#ifdef DEBUG
599		xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i));
600#endif
601		return FALSE;
602	    }
603#ifdef DEBUG
604	    xf86ErrorF("Recieved: 0x%x\n",c);
605#endif
606	    if (c == 0xFA) /* ACK */
607		break;
608
609	    if (c == 0xFE) /* resend */
610		continue;
611
612
613	    if (c == 0xFC) /* error */
614		return FALSE;
615
616	    /* Some mice accidently enter wrap mode during init */
617	    if (c == *(bytes + i)    /* wrap mode */
618		&& (*(bytes + i) != 0xEC)) /* avoid recursion */
619		ps2DisableWrapMode(pInfo);
620
621	    return FALSE;
622	}
623	if (j == 10)
624	    return FALSE;
625    }
626
627    return TRUE;
628}
629
630static Bool
631ps2DisableDataReporting(InputInfoPtr pInfo)
632{
633    unsigned char packet[] = { 0xF5 };
634    return ps2SendPacket(pInfo, packet, sizeof(packet));
635}
636
637Bool
638ps2EnableDataReporting(InputInfoPtr pInfo)
639{
640    unsigned char packet[] = { 0xF4 };
641    return ps2SendPacket(pInfo, packet, sizeof(packet));
642}
643
644int
645ps2GetDeviceID(InputInfoPtr pInfo)
646{
647    unsigned char u;
648    unsigned char packet[] = { 0xf2 };
649
650    usleep(30000);
651    xf86FlushInput(pInfo->fd);
652    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
653	return -1;
654    while (1) {
655	if (!readMouse(pInfo,&u))
656	    return -1;
657	if (u != 0xFA)
658	    break;
659    }
660#ifdef DEBUG
661    xf86ErrorF("Obtained Mouse Type: %x\n",u);
662#endif
663    return (int) u;
664}
665
666Bool
667ps2Reset(InputInfoPtr pInfo)
668{
669    unsigned char u;
670    unsigned char packet[] = { 0xff };
671    unsigned char reply[] = { 0xaa, 0x00 };
672    unsigned int i;
673#ifdef DEBUG
674   xf86ErrorF("PS/2 Mouse reset\n");
675#endif
676    if (!ps2SendPacket(pInfo, packet, sizeof(packet)))
677	return FALSE;
678    /* we need a little delay here */
679    xf86WaitForInput(pInfo->fd, 500000);
680    for (i = 0; i < sizeof(reply) ; i++) {
681	if (!readMouse(pInfo,&u)) {
682	    goto EXIT;
683	}
684	if (u != reply[i])
685	    goto EXIT;
686    }
687    return TRUE;
688
689 EXIT:
690    xf86FlushInput(pInfo->fd);
691    return FALSE;
692}
693
694static MouseProtocolID
695probePs2ProtocolPnP(InputInfoPtr pInfo)
696{
697    unsigned char u;
698    MouseProtocolID ret = PROT_UNKNOWN;
699
700    xf86FlushInput(pInfo->fd);
701
702    ps2DisableDataReporting(pInfo);
703
704    if (ps2Reset(pInfo)) { /* Reset PS2 device */
705  	unsigned char seq[] = { 243, 200, 243, 100, 243, 80 };
706	/* Try to identify Intelli Mouse */
707	if (ps2SendPacket(pInfo, seq, sizeof(seq))) {
708	    u = ps2GetDeviceID(pInfo);
709	    if (u == 0x03) {
710		/* found IntelliMouse now try IntelliExplorer */
711		unsigned char seq[] = { 243, 200, 243, 200, 243, 80 };
712		if (ps2SendPacket(pInfo,seq,sizeof(seq))) {
713		    u = ps2GetDeviceID(pInfo);
714		    if (u == 0x04)
715			ret =  PROT_EXPPS2;
716		    else
717			ret = PROT_IMPS2;
718		}
719	    } else if (ps2Reset(pInfo))  /* reset again to find sane state */
720		ret = PROT_PS2;
721	}
722
723	if (ret != PROT_UNKNOWN)
724	    ps2EnableDataReporting(pInfo);
725    }
726    return ret;
727}
728
729static struct ps2protos {
730    int Id;
731    MouseProtocolID protoID;
732} ps2 [] = {
733    { 0x0, PROT_PS2 },
734    { 0x3, PROT_IMPS2 },
735    { 0x4, PROT_EXPPS2 },
736    { -1 , PROT_UNKNOWN }
737};
738
739
740static MouseProtocolID
741getPs2ProtocolPnP(InputInfoPtr pInfo)
742{
743    int Id;
744    int i;
745    MouseProtocolID proto;
746    int count = 4;
747
748    xf86FlushInput(pInfo->fd);
749
750    while (--count)
751	if (ps2DisableDataReporting(pInfo))
752	    break;
753
754    if (!count) {
755	proto = PROT_UNKNOWN;
756	goto EXIT;
757    }
758
759    if ((Id = ps2GetDeviceID(pInfo)) == -1) {
760	proto = PROT_UNKNOWN;
761	goto EXIT;
762    }
763
764    if (-1 == ps2EnableDataReporting(pInfo)) {
765	proto = PROT_UNKNOWN;
766	goto EXIT;
767    }
768
769    for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) {
770	if (ps2[i].Id == Id) {
771	    xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id);
772	    proto =  ps2[i].protoID;
773	    goto EXIT;
774	}
775    }
776
777    proto = PROT_UNKNOWN;
778    xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id);
779
780 EXIT:
781    xf86FlushInput(pInfo->fd);
782    return proto;
783}
784
785