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 <xorg-server.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
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 "mouse.h"
40 #include "mousePriv.h"
41
42 #ifdef MOUSEINITDEBUG
43 # define DEBUG
44 # define EXTMOUSEDEBUG
45 #endif
46
47 /* serial PnP ID string */
48 typedef struct {
49 int revision; /* PnP revision, 100 for 1.00 */
50 char *eisaid; /* EISA ID including mfr ID and product ID */
51 char *serial; /* serial No, optional */
52 char *class; /* device class, optional */
53 char *compat; /* list of compatible drivers, optional */
54 char *description; /* product description, optional */
55 int neisaid; /* length of the above fields... */
56 int nserial;
57 int nclass;
58 int ncompat;
59 int ndescription;
60 } pnpid_t;
61
62 /* symbol table entry */
63 typedef struct {
64 const char *name;
65 MouseProtocolID val;
66 } symtab_t;
67
68 /* PnP EISA/product IDs */
69 static symtab_t pnpprod[] = {
70 { "KML0001", PROT_THINKING }, /* Kensington ThinkingMouse */
71 { "MSH0001", PROT_IMSERIAL }, /* MS IntelliMouse */
72 { "MSH0004", PROT_IMSERIAL }, /* MS IntelliMouse TrackBall */
73 { "KYEEZ00", PROT_MS }, /* Genius EZScroll */
74 { "KYE0001", PROT_MS }, /* Genius PnP Mouse */
75 { "KYE0002", PROT_MS }, /* MouseSystem (Genius?) SmartScroll */
76 { "KYE0003", PROT_IMSERIAL }, /* Genius NetMouse */
77 { "KYE0004", PROT_IMSERIAL }, /* Genius NetScroll+ (serial) */
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
134 static 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
145 static int pnpgets(InputInfoPtr, char *, Bool *prePNP);
146 static int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
147 static MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf);
148 static symtab_t *pnpproto(pnpid_t *);
149 static symtab_t *gettoken(symtab_t *, char *, int);
150 static MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo);
151 static MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo);
152
153 static MouseProtocolID
MouseGetSerialPnpProtocol(InputInfoPtr pInfo)154 MouseGetSerialPnpProtocol(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
177 MouseProtocolID
MouseGetPnpProtocol(InputInfoPtr pInfo)178 MouseGetPnpProtocol(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 */
233 static int
pnpgets(InputInfoPtr pInfo,char * buf,Bool * prePNP)234 pnpgets(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 */
367 disconnect_idle:
368 i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */
369 xf86SerialModemSetBits(pInfo->fd, i);
370 connect_idle:
371 return 0;
372 }
373
374 static int
pnpparse(InputInfoPtr pInfo,pnpid_t * id,char * buf,int len)375 pnpparse(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 */
500 static MouseProtocolID
prepnpparse(InputInfoPtr pInfo,char * buf)501 prepnpparse(InputInfoPtr pInfo, char *buf)
502 {
503 if (buf[0] == 'M' && buf[1] == '3')
504 return PROT_MS;
505 return PROT_UNKNOWN;
506 }
507
508
509 static symtab_t *
pnpproto(pnpid_t * id)510 pnpproto(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
548 static symtab_t *
gettoken(symtab_t * tab,char * s,int len)549 gettoken(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
562 static int
readMouse(InputInfoPtr pInfo,unsigned char * u)563 readMouse(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
573 static void
ps2DisableWrapMode(InputInfoPtr pInfo)574 ps2DisableWrapMode(InputInfoPtr pInfo)
575 {
576 unsigned char reset_wrap_mode[] = { 0xEC };
577 ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode));
578 }
579
580 Bool
ps2SendPacket(InputInfoPtr pInfo,unsigned char * bytes,int len)581 ps2SendPacket(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
630 static Bool
ps2DisableDataReporting(InputInfoPtr pInfo)631 ps2DisableDataReporting(InputInfoPtr pInfo)
632 {
633 unsigned char packet[] = { 0xF5 };
634 return ps2SendPacket(pInfo, packet, sizeof(packet));
635 }
636
637 Bool
ps2EnableDataReporting(InputInfoPtr pInfo)638 ps2EnableDataReporting(InputInfoPtr pInfo)
639 {
640 unsigned char packet[] = { 0xF4 };
641 return ps2SendPacket(pInfo, packet, sizeof(packet));
642 }
643
644 int
ps2GetDeviceID(InputInfoPtr pInfo)645 ps2GetDeviceID(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
666 Bool
ps2Reset(InputInfoPtr pInfo)667 ps2Reset(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
694 static MouseProtocolID
probePs2ProtocolPnP(InputInfoPtr pInfo)695 probePs2ProtocolPnP(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 im_seq[] = { 243, 200, 243, 200, 243, 80 };
712 if (ps2SendPacket(pInfo,im_seq,sizeof(im_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
729 static 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
740 static MouseProtocolID
getPs2ProtocolPnP(InputInfoPtr pInfo)741 getPs2ProtocolPnP(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