1 /* $OpenBSD: mouse_protocols.c,v 1.10 2004/12/05 04:03:53 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2001 Jean-Baptiste Marchand, Julien Montagne and Jerome Verdon
5 *
6 * Copyright (c) 1998 by Kazutaka Yokota
7 *
8 * Copyright (c) 1995 Michael Smith
9 *
10 * Copyright (c) 1993 by David Dawes <dawes@xfree86.org>
11 *
12 * Copyright (c) 1990,91 by Thomas Roell, Dinkelscherben, Germany.
13 *
14 * All rights reserved.
15 *
16 * Most of this code was taken from the FreeBSD moused daemon, written by
17 * Michael Smith. The FreeBSD moused daemon already contained code from the
18 * Xfree Project, written by David Dawes and Thomas Roell and Kazutaka Yokota.
19 *
20 * Adaptation to OpenBSD was done by Jean-Baptiste Marchand, Julien Montagne
21 * and Jerome Verdon.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 * 3. All advertising materials mentioning features or use of this software
32 * must display the following acknowledgement:
33 * This product includes software developed by
34 * David Dawes, Jean-Baptiste Marchand, Julien Montagne, Thomas Roell,
35 * Michael Smith, Jerome Verdon and Kazutaka Yokota.
36 * 4. The name authors may not be used to endorse or promote products
37 * derived from this software without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
40 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
42 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
43 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49 *
50 *
51 */
52
53 /* Support for non-wsmouse (i.e. serial mice) mice */
54
55 /*
56 * Most of this code comes from the Xfree Project and are derived from two files
57 * of Xfree86 3.3.6 with the following CVS tags :
58 $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.21.2.24
59 1999/12/11 19:00:42 hohndel Exp $
60 and
61 $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_PnPMouse.c,v 1.1.2.6
62 1999/07/29 09:22:51 hohndel Exp $
63 */
64
65 #include <sys/ioctl.h>
66 #include <sys/types.h>
67 #include <sys/time.h>
68 #include <sys/tty.h>
69
70 #include <ctype.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <unistd.h>
75 #include <setjmp.h>
76 #include <signal.h>
77 #include <poll.h>
78 #include <stdio.h>
79 #include <string.h>
80 #include <stdlib.h>
81 #include <syslog.h>
82
83 #include "wsmoused.h"
84 #include "mouse_protocols.h"
85
86 extern int debug;
87 extern int nodaemon;
88 extern int background;
89 extern mouse_t mouse;
90
91 /* Cflags of each mouse protocol, ordered by P_XXX */
92 static unsigned short mousecflags[] = {
93 (CS7 | CREAD | CLOCAL | HUPCL), /* Microsoft */
94 (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* MouseSystems */
95 (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* Logitech */
96 (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL), /* MMSeries */
97 (CS7 | CREAD | CLOCAL | HUPCL), /* MouseMan */
98 (CS8 | CREAD | CLOCAL | HUPCL), /* MM HitTablet */
99 (CS7 | CREAD | CLOCAL | HUPCL), /* GlidePoint */
100 (CS7 | CREAD | CLOCAL | HUPCL), /* IntelliMouse */
101 (CS7 | CREAD | CLOCAL | HUPCL), /* Thinking Mouse */
102 };
103
104 /* array ordered by P_XXX giving protocol properties */
105 static unsigned char proto[][7] = {
106 /* mask hd_id dp_mask dp_id bytes b4_mask b4_id */
107 {0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00}, /* Microsoft */
108 {0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff}, /* MouseSystems */
109 {0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff}, /* Logitech */
110 {0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff}, /* MMSeries */
111 {0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00}, /* MouseMan */
112 {0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff}, /* MM HitTablet */
113 {0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00}, /* GlidePoint */
114 {0x40, 0x40, 0x40, 0x00, 3, ~0x3f, 0x00}, /* IntelliMouse */
115 {0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00}, /* ThinkingMouse */
116 };
117
118 /*
119 * array ordered by P_XXX (mouse protocols) giving the protocol
120 * corresponding to the name of a mouse
121 */
122 char *mouse_names[] = {
123 "microsoft",
124 "mousesystems",
125 "logitech",
126 "mmseries",
127 "mouseman",
128 "mmhitab",
129 "glidepoint",
130 "intellimouse",
131 "thinkingmouse",
132 NULL
133 };
134
135 /* protocol currently used */
136 static unsigned char cur_proto[7];
137
138 /* PnP EISA/product IDs */
139 static symtab_t pnpprod[] = {
140 {"KML0001", P_THINKING},/* Kensignton ThinkingMouse */
141 {"MSH0001", P_IMSERIAL},/* MS IntelliMouse */
142 {"MSH0004", P_IMSERIAL},/* MS IntelliMouse TrackBall */
143 {"KYEEZ00", P_MS}, /* Genius EZScroll */
144 {"KYE0001", P_MS}, /* Genius PnP Mouse */
145 {"KYE0003", P_IMSERIAL},/* Genius NetMouse */
146 {"LGI800C", P_IMSERIAL},/* Logitech MouseMan (4 button model) */
147 {"LGI8050", P_IMSERIAL},/* Logitech MouseMan+ */
148 {"LGI8051", P_IMSERIAL},/* Logitech FirstMouse+ */
149 {"LGI8001", P_LOGIMAN}, /* Logitech serial */
150 {"PNP0F01", P_MS}, /* MS serial */
151 /*
152 * XXX EzScroll returns PNP0F04 in the compatible device field; but it
153 * doesn't look compatible...
154 */
155 {"PNP0F04", P_MSC}, /* MouseSystems */
156 {"PNP0F05", P_MSC}, /* MouseSystems */
157 {"PNP0F08", P_LOGIMAN}, /* Logitech serial */
158 {"PNP0F09", P_MS}, /* MS BallPoint serial */
159 {"PNP0F0A", P_MS}, /* MS PnP serial */
160 {"PNP0F0B", P_MS}, /* MS PnP BallPoint serial */
161 {"PNP0F0C", P_MS}, /* MS serial comatible */
162 {"PNP0F0F", P_MS}, /* MS BallPoint comatible */
163 {"PNP0F17", P_LOGIMAN}, /* Logitech serial compat */
164 {NULL, -1},
165 };
166
167 static symtab_t *
gettoken(symtab_t * tab,char * s,int len)168 gettoken(symtab_t * tab, char *s, int len)
169 {
170 int i;
171
172 for (i = 0; tab[i].name != NULL; ++i) {
173 if (strncmp(tab[i].name, s, len) == 0)
174 break;
175 }
176 return &tab[i];
177 }
178
179 char *
mouse_name(int type)180 mouse_name(int type)
181 {
182 return (type == P_UNKNOWN ||
183 (type > sizeof(mouse_names) / sizeof(mouse_names[0]) - 1)) ?
184 "unknown" : mouse_names[type];
185 }
186
187 void
SetMouseSpeed(int old,int new,unsigned int cflag)188 SetMouseSpeed(int old, int new, unsigned int cflag)
189 {
190 struct termios tty;
191 char *c;
192
193 if (!IS_SERIAL_DEV(mouse.portname))
194 return;
195
196 if (tcgetattr(mouse.mfd, &tty) < 0) {
197 debug("Warning: %s unable to get status of mouse fd (%s)\n",
198 mouse.portname, strerror(errno));
199 return;
200 }
201 /* this will query the initial baudrate only once */
202 if (mouse.old_baudrate < 0) {
203 switch (cfgetispeed(&tty)) {
204 case B9600:
205 mouse.old_baudrate = 9600;
206 break;
207 case B4800:
208 mouse.old_baudrate = 4800;
209 break;
210 case B2400:
211 mouse.old_baudrate = 2400;
212 break;
213 case B1200:
214 default:
215 mouse.old_baudrate = 1200;
216 break;
217 }
218 }
219 tty.c_iflag = IGNBRK | IGNPAR;
220 tty.c_oflag = 0;
221 tty.c_lflag = 0;
222 tty.c_cflag = (tcflag_t) cflag;
223 tty.c_cc[VTIME] = 0;
224 tty.c_cc[VMIN] = 1;
225
226 switch (old) {
227 case 9600:
228 cfsetispeed(&tty, B9600);
229 cfsetospeed(&tty, B9600);
230 break;
231 case 4800:
232 cfsetispeed(&tty, B4800);
233 cfsetospeed(&tty, B4800);
234 break;
235 case 2400:
236 cfsetispeed(&tty, B2400);
237 cfsetospeed(&tty, B2400);
238 break;
239 case 1200:
240 default:
241 cfsetispeed(&tty, B1200);
242 cfsetospeed(&tty, B1200);
243 }
244
245 if (tcsetattr(mouse.mfd, TCSADRAIN, &tty) < 0)
246 logerr(1, "unable to get mouse status. Exiting...\n");
247
248 switch (new) {
249 case 9600:
250 c = "*q";
251 cfsetispeed(&tty, B9600);
252 cfsetospeed(&tty, B9600);
253 break;
254 case 4800:
255 c = "*p";
256 cfsetispeed(&tty, B4800);
257 cfsetospeed(&tty, B4800);
258 break;
259 case 2400:
260 c = "*o";
261 cfsetispeed(&tty, B2400);
262 cfsetospeed(&tty, B2400);
263 break;
264 case 1200:
265 default:
266 c = "*n";
267 cfsetispeed(&tty, B1200);
268 cfsetospeed(&tty, B1200);
269 }
270
271 if (mouse.proto == P_LOGIMAN || mouse.proto == P_LOGI) {
272 if (write(mouse.mfd, c, 2) != 2)
273 logerr(1, "unable to write to mouse. Exiting...\n");
274 }
275 usleep(100000);
276
277 if (tcsetattr(mouse.mfd, TCSADRAIN, &tty) < 0)
278 logerr(1, "unable to get mouse status. Exiting...\n");
279 }
280
281 int
FlushInput(int fd)282 FlushInput(int fd)
283 {
284 struct pollfd pfd[1];
285 char c[4];
286
287 if (tcflush(fd, TCIFLUSH) == 0)
288 return 0;
289
290 pfd[0].fd = fd;
291 pfd[0].events = POLLIN;
292
293 while (poll(pfd, 1, 0) > 0)
294 read(fd, &c, sizeof(c));
295 return 0;
296 }
297
298 /*
299 * Try to elicit a PnP ID as described in
300 * Microsoft, Hayes: "Plug and Play External COM Device Specification,
301 * rev 1.00", 1995.
302 *
303 * The routine does not fully implement the COM Enumerator as par Section
304 * 2.1 of the document. In particular, we don't have idle state in which
305 * the driver software monitors the com port for dynamic connection or
306 * removal of a device at the port, because `moused' simply quits if no
307 * device is found.
308 *
309 * In addition, as PnP COM device enumeration procedure slightly has
310 * changed since its first publication, devices which follow earlier
311 * revisions of the above spec. may fail to respond if the rev 1.0
312 * procedure is used. XXX
313 */
314 static int
pnpgets(int mouse_fd,char * buf)315 pnpgets(int mouse_fd, char *buf)
316 {
317 struct pollfd pfd[1];
318 int i;
319 char c;
320
321 pfd[0].fd = mouse_fd;
322 pfd[0].events = POLLIN;
323
324 #if 0
325 /*
326 * This is the procedure described in rev 1.0 of PnP COM device spec.
327 * Unfortunately, some devices which comform to earlier revisions of
328 * the spec gets confused and do not return the ID string...
329 */
330
331 /* port initialization (2.1.2) */
332 ioctl(mouse_fd, TIOCMGET, &i);
333 i |= TIOCM_DTR; /* DTR = 1 */
334 i &= ~TIOCM_RTS; /* RTS = 0 */
335 ioctl(mouse_fd, TIOCMSET, &i);
336 usleep(200000);
337 if ((ioctl(mouse_fd, TIOCMGET, &i) == -1) || ((i & TIOCM_DSR) == 0))
338 goto disconnect_idle;
339
340 /* port setup, 1st phase (2.1.3) */
341 SetMouseSpeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
342 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
343 ioctl(mouse_fd, TIOCMBIC, &i);
344 usleep(200000);
345 i = TIOCM_DTR; /* DTR = 1, RTS = 0 */
346 ioctl(mouse_fd, TIOCMBIS, &i);
347 usleep(200000);
348
349 /* wait for response, 1st phase (2.1.4) */
350 FlushInput(mouse_fd);
351 i = TIOCM_RTS; /* DTR = 1, RTS = 1 */
352 ioctl(mouse_fd, TIOCMBIS, &i);
353
354 /* try to read something */
355 if (poll(pfd, 1, 200000 / 1000) <= 0) {
356 /* port setup, 2nd phase (2.1.5) */
357 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
358 ioctl(mouse_fd, TIOCMBIC, &i);
359 usleep(200000);
360
361 /* wait for respose, 2nd phase (2.1.6) */
362 FlushInput(mouse_fd);
363 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
364 ioctl(mouse_fd, TIOCMBIS, &i);
365
366 /* try to read something */
367 if (poll(pfd, 1, 200000 / 1000) <= 0)
368 goto connect_idle;
369 }
370 #else
371
372 /*
373 * This is a simplified procedure; it simply toggles RTS.
374 */
375 SetMouseSpeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
376
377 ioctl(mouse_fd, TIOCMGET, &i);
378 i |= TIOCM_DTR; /* DTR = 1 */
379 i &= ~TIOCM_RTS; /* RTS = 0 */
380 ioctl(mouse_fd, TIOCMSET, &i);
381 usleep(200000);
382
383 /* wait for response */
384 FlushInput(mouse_fd);
385 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
386 ioctl(mouse_fd, TIOCMBIS, &i);
387
388 /* try to read something */
389 if (poll(pfd, 1, 200000 / 1000) <= 0)
390 goto connect_idle;
391 #endif
392
393 /* collect PnP COM device ID (2.1.7) */
394 i = 0;
395 usleep(200000); /* the mouse must send `Begin ID' within
396 * 200msec */
397 while (read(mouse_fd, &c, 1) == 1) {
398 /* we may see "M", or "M3..." before `Begin ID' */
399 if ((c == 0x08) || (c == 0x28)) { /* Begin ID */
400 buf[i++] = c;
401 break;
402 }
403 }
404 if (i <= 0) {
405 /* we haven't seen `Begin ID' in time... */
406 goto connect_idle;
407 }
408 ++c; /* make it `End ID' */
409 for (;;) {
410 if (poll(pfd, 1, 200000 / 1000) <= 0)
411 break;
412
413 read(mouse_fd, &buf[i], 1);
414 if (buf[i++] == c) /* End ID */
415 break;
416 if (i >= 256)
417 break;
418 }
419 if (buf[i - 1] != c)
420 goto connect_idle;
421 return i;
422
423 #if 0
424 /*
425 * According to PnP spec, we should set DTR = 1 and RTS = 0 while
426 * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed,
427 * assuming there is something at the port even if it didn't
428 * respond to the PnP enumeration procedure.
429 */
430 disconnect_idle:
431 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
432 ioctl(mouse_fd, TIOCMBIS, &i);
433 #endif
434
435 connect_idle:
436 return 0;
437 }
438
439 /* pnpparse : parse a PnP string ID */
440 static int
pnpparse(pnpid_t * id,char * buf,int len)441 pnpparse(pnpid_t * id, char *buf, int len)
442 {
443 char s[3];
444 int offset, sum = 0, i, j;
445
446 id->revision = 0;
447 id->eisaid = NULL;
448 id->serial = NULL;
449 id->class = NULL;
450 id->compat = NULL;
451 id->description = NULL;
452 id->neisaid = 0;
453 id->nserial = 0;
454 id->nclass = 0;
455 id->ncompat = 0;
456 id->ndescription = 0;
457
458 offset = 0x28 - buf[0];
459
460 /* calculate checksum */
461 for (i = 0; i < len - 3; ++i) {
462 sum += buf[i];
463 buf[i] += offset;
464 }
465 sum += buf[len - 1];
466 for (; i < len; ++i)
467 buf[i] += offset;
468 debug("Mouse: PnP ID string: '%*.*s'\n", len, len, buf);
469
470 /* revision */
471 buf[1] -= offset;
472 buf[2] -= offset;
473 id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
474 debug("Mouse: PnP rev %d.%02d\n", id->revision / 100, id->revision % 100);
475
476 /* EISA vender and product ID */
477 id->eisaid = &buf[3];
478 id->neisaid = 7;
479
480 /* option strings */
481 i = 10;
482 if (buf[i] == '\\') {
483 /* device serial # */
484 for (j = ++i; i < len; ++i) {
485 if (buf[i] == '\\')
486 break;
487 }
488 if (i >= len)
489 i -= 3;
490 if (i - j == 8) {
491 id->serial = &buf[j];
492 id->nserial = 8;
493 }
494 }
495 if (buf[i] == '\\') {
496 /* PnP class */
497 for (j = ++i; i < len; ++i) {
498 if (buf[i] == '\\')
499 break;
500 }
501 if (i >= len)
502 i -= 3;
503 if (i > j + 1) {
504 id->class = &buf[j];
505 id->nclass = i - j;
506 }
507 }
508 if (buf[i] == '\\') {
509 /* compatible driver */
510 for (j = ++i; i < len; ++i) {
511 if (buf[i] == '\\')
512 break;
513 }
514 /*
515 * PnP COM spec prior to v0.96 allowed '*' in this field,
516 * it's not allowed now; just ignore it.
517 */
518 if (buf[j] == '*')
519 ++j;
520 if (i >= len)
521 i -= 3;
522 if (i > j + 1) {
523 id->compat = &buf[j];
524 id->ncompat = i - j;
525 }
526 }
527 if (buf[i] == '\\') {
528 /* product description */
529 for (j = ++i; i < len; ++i) {
530 if (buf[i] == ';')
531 break;
532 }
533 if (i >= len)
534 i -= 3;
535 if (i > j + 1) {
536 id->description = &buf[j];
537 id->ndescription = i - j;
538 }
539 }
540 /* checksum exists if there are any optional fields */
541 if ((id->nserial > 0) || (id->nclass > 0) ||
542 (id->ncompat > 0) || (id->ndescription > 0)) {
543 #if 0
544 debug("Mouse: PnP checksum: 0x%02X\n", sum);
545 #endif
546 snprintf(s, sizeof s, "%02X", sum & 0x0ff);
547 if (strncmp(s, &buf[len - 3], 2) != 0) {
548 #if 0
549 /*
550 * Checksum error!!
551 * I found some mice do not comply with the PnP COM device
552 * spec regarding checksum... XXX
553 */
554 return FALSE;
555 #endif
556 }
557 }
558 return 1;
559 }
560
561 /* pnpproto : return the prototype used, based on the PnP ID string */
562 static symtab_t *
pnpproto(pnpid_t * id)563 pnpproto(pnpid_t * id)
564 {
565 symtab_t *t;
566 int i, j;
567
568 if (id->nclass > 0)
569 if (strncmp(id->class, "MOUSE", id->nclass) != 0)
570 /* this is not a mouse! */
571 return NULL;
572
573 if (id->neisaid > 0) {
574 t = gettoken(pnpprod, id->eisaid, id->neisaid);
575 if (t->val != -1)
576 return t;
577 }
578 /*
579 * The 'Compatible drivers' field may contain more than one
580 * ID separated by ','.
581 */
582 if (id->ncompat <= 0)
583 return NULL;
584 for (i = 0; i < id->ncompat; ++i) {
585 for (j = i; id->compat[i] != ','; ++i)
586 if (i >= id->ncompat)
587 break;
588 if (i > j) {
589 t = gettoken(pnpprod, id->compat + j, i - j);
590 if (t->val != -1)
591 return t;
592 }
593 }
594
595 return NULL;
596 }
597
598 /* mouse_init : init the mouse by writing appropriate sequences */
599 void
mouse_init(void)600 mouse_init(void)
601 {
602 struct pollfd pfd[1];
603 char *s;
604 char c;
605 int i;
606
607 if (IS_WSMOUSE_DEV(mouse.portname)) {
608 wsmouse_init();
609 return;
610 }
611 pfd[0].fd = mouse.mfd;
612 pfd[0].events = POLLIN;
613
614 /**
615 ** This comment is a little out of context here, but it contains
616 ** some useful information...
617 ********************************************************************
618 **
619 ** The following lines take care of the Logitech MouseMan protocols.
620 **
621 ** NOTE: There are diffrent versions of both MouseMan and TrackMan!
622 ** Hence I add another protocol P_LOGIMAN, which the user can
623 ** specify as MouseMan in his XF86Config file. This entry was
624 ** formerly handled as a special case of P_MS. However, people
625 ** who don't have the middle button problem, can still specify
626 ** Microsoft and use P_MS.
627 **
628 ** By default, these mice should use a 3 byte Microsoft protocol
629 ** plus a 4th byte for the middle button. However, the mouse might
630 ** have switched to a different protocol before we use it, so I send
631 ** the proper sequence just in case.
632 **
633 ** NOTE: - all commands to (at least the European) MouseMan have to
634 ** be sent at 1200 Baud.
635 ** - each command starts with a '*'.
636 ** - whenever the MouseMan receives a '*', it will switch back
637 ** to 1200 Baud. Hence I have to select the desired protocol
638 ** first, then select the baud rate.
639 **
640 ** The protocols supported by the (European) MouseMan are:
641 ** - 5 byte packed binary protocol, as with the Mouse Systems
642 ** mouse. Selected by sequence "*U".
643 ** - 2 button 3 byte Microsoft compatible protocol. Selected
644 ** by sequence "*V".
645 ** - 3 button 3+1 byte Microsoft compatible protocol (default).
646 ** Selected by sequence "*X".
647 **
648 ** The following baud rates are supported:
649 ** - 1200 Baud (default). Selected by sequence "*n".
650 ** - 9600 Baud. Selected by sequence "*q".
651 **
652 ** Selecting a sample rate is no longer supported with the MouseMan!
653 ** Some additional lines in xf86Config.c take care of ill configured
654 ** baud rates and sample rates. (The user will get an error.)
655 */
656
657 switch (mouse.proto) {
658
659 case P_LOGI:
660 /*
661 * The baud rate selection command must be sent at the current
662 * baud rate; try all likely settings
663 */
664 SetMouseSpeed(9600, mouse.baudrate, mousecflags[mouse.proto]);
665 SetMouseSpeed(4800, mouse.baudrate, mousecflags[mouse.proto]);
666 SetMouseSpeed(2400, mouse.baudrate, mousecflags[mouse.proto]);
667 #if 0
668 SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]);
669 #endif
670 /* select MM series data format */
671 write(mouse.mfd, "S", 1);
672 SetMouseSpeed(mouse.baudrate, mouse.baudrate,
673 mousecflags[P_MM]);
674 /* select report rate/frequency */
675 if (mouse.rate <= 0)
676 write(mouse.mfd, "O", 1);
677 else if (mouse.rate <= 15)
678 write(mouse.mfd, "J", 1);
679 else if (mouse.rate <= 27)
680 write(mouse.mfd, "K", 1);
681 else if (mouse.rate <= 42)
682 write(mouse.mfd, "L", 1);
683 else if (mouse.rate <= 60)
684 write(mouse.mfd, "R", 1);
685 else if (mouse.rate <= 85)
686 write(mouse.mfd, "M", 1);
687 else if (mouse.rate <= 125)
688 write(mouse.mfd, "Q", 1);
689 else
690 write(mouse.mfd, "N", 1);
691 break;
692
693 case P_LOGIMAN:
694 /* The command must always be sent at 1200 baud */
695 SetMouseSpeed(1200, 1200, mousecflags[mouse.proto]);
696 write(mouse.mfd, "*X", 2);
697 SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]);
698 break;
699
700 case P_MMHIT:
701 SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]);
702
703 /*
704 * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
705 * The tablet must be configured to be in MM mode, NO parity,
706 * Binary Format. xf86Info.sampleRate controls the sensativity
707 * of the tablet. We only use this tablet for it's 4-button puck
708 * so we don't run in "Absolute Mode"
709 */
710 write(mouse.mfd, "z8", 2); /* Set Parity = "NONE" */
711 usleep(50000);
712 write(mouse.mfd, "zb", 2); /* Set Format = "Binary" */
713 usleep(50000);
714 write(mouse.mfd, "@", 1); /* Set Report Mode = "Stream" */
715 usleep(50000);
716 write(mouse.mfd, "R", 1); /* Set Output Rate = "45 rps" */
717 usleep(50000);
718 write(mouse.mfd, "I\x20", 2); /* Set Incrememtal Mode "20" */
719 usleep(50000);
720 write(mouse.mfd, "E", 1); /* Set Data Type = "Relative */
721 usleep(50000);
722
723 /* Resolution is in 'lines per inch' on the Hitachi tablet */
724 if (mouse.resolution == MOUSE_RES_LOW)
725 c = 'g';
726 else if (mouse.resolution == MOUSE_RES_MEDIUMLOW)
727 c = 'e';
728 else if (mouse.resolution == MOUSE_RES_MEDIUMHIGH)
729 c = 'h';
730 else if (mouse.resolution == MOUSE_RES_HIGH)
731 c = 'd';
732 else if (mouse.resolution <= 40)
733 c = 'g';
734 else if (mouse.resolution <= 100)
735 c = 'd';
736 else if (mouse.resolution <= 200)
737 c = 'e';
738 else if (mouse.resolution <= 500)
739 c = 'h';
740 else if (mouse.resolution <= 1000)
741 c = 'j';
742 else
743 c = 'd';
744 write(mouse.mfd, &c, 1);
745 usleep(50000);
746
747 write(mouse.mfd, "\021", 1); /* Resume DATA output */
748 break;
749
750 case P_THINKING:
751 SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]);
752 /* the PnP ID string may be sent again, discard it */
753 usleep(200000);
754 i = FREAD;
755 ioctl(mouse.mfd, TIOCFLUSH, &i);
756 /* send the command to initialize the beast */
757 for (s = "E5E5"; *s; ++s) {
758 write(mouse.mfd, s, 1);
759
760 if (poll(pfd, 1, INFTIM) <= 0)
761 break;
762 read(mouse.mfd, &c, 1);
763 debug("%c", c);
764 if (c != *s)
765 break;
766 }
767 break;
768
769 case P_MSC:
770 SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]);
771 #if 0
772 if (mouse.flags & ClearDTR) {
773 i = TIOCM_DTR;
774 ioctl(mouse.mfd, TIOCMBIC, &i);
775 }
776 if (mouse.flags & ClearRTS) {
777 i = TIOCM_RTS;
778 ioctl(mouse.mfd, TIOCMBIC, &i);
779 }
780 #endif
781 break;
782
783 default:
784 SetMouseSpeed(1200, mouse.baudrate, mousecflags[mouse.proto]);
785 break;
786 }
787 }
788
789 /* mouse_identify : identify the protocol used by the mouse */
790 int
mouse_identify(void)791 mouse_identify(void)
792 {
793 char pnpbuf[256]; /* PnP identifier string may be up to
794 * 256 bytes long */
795 pnpid_t pnpid;
796 symtab_t *t;
797 int len;
798
799 /* protocol has been specified with '-t' */
800 if (mouse.proto != P_UNKNOWN)
801 memmove(cur_proto, proto[mouse.proto], sizeof(cur_proto));
802 else {
803 /* maybe this is an PnP mouse... */
804 if (mouse.flags & NoPnP)
805 return mouse.proto;
806 if (((len = pnpgets(mouse.mfd, pnpbuf)) <= 0)
807 || !pnpparse(&pnpid, pnpbuf, len))
808 return mouse.proto;
809
810 debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
811 pnpid.neisaid, pnpid.neisaid,
812 pnpid.eisaid, pnpid.ncompat,
813 pnpid.ncompat, pnpid.compat,
814 pnpid.ndescription, pnpid.ndescription,
815 pnpid.description);
816
817 /* we have a valid PnP serial device ID */
818 t = pnpproto(&pnpid);
819 if (t != NULL) {
820 mouse.proto = t->val;
821 memmove(cur_proto, proto[mouse.proto], sizeof(cur_proto));
822 } else
823 mouse.proto = P_UNKNOWN;
824
825 }
826
827 debug("proto params: %02x %02x %02x %02x %d %02x %02x",
828 cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
829 cur_proto[4], cur_proto[5], cur_proto[6]);
830
831 return mouse.proto;
832 }
833
834 /* mouse_protocol : decode bytes with the current mouse protocol */
835 int
mouse_protocol(u_char rBuf,mousestatus_t * act)836 mouse_protocol(u_char rBuf, mousestatus_t * act)
837 {
838 /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
839 static int butmapmss[4] = { /* Microsoft, MouseMan,
840 * GlidePoint, IntelliMouse,
841 * Thinking Mouse */
842 0,
843 MOUSE_BUTTON3DOWN,
844 MOUSE_BUTTON1DOWN,
845 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
846 };
847 static int butmapmss2[4] = { /* Microsoft, MouseMan,
848 * GlidePoint, Thinking Mouse */
849 0,
850 MOUSE_BUTTON4DOWN,
851 MOUSE_BUTTON2DOWN,
852 MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
853 };
854 /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
855 static int butmapintelli[4] = { /* IntelliMouse, NetMouse,
856 * Mie Mouse, MouseMan+ */
857 0,
858 MOUSE_BUTTON2DOWN,
859 MOUSE_BUTTON4DOWN,
860 MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
861 };
862 /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
863 static int butmapmsc[8] = { /* MouseSystems, MMSeries,
864 * Logitech, Bus, sysmouse */
865 0,
866 MOUSE_BUTTON3DOWN,
867 MOUSE_BUTTON2DOWN,
868 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
869 MOUSE_BUTTON1DOWN,
870 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
871 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
872 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
873 };
874 /* for Hitachi tablet */
875 static int butmaphit[8] = { /* MM HitTablet */
876 0,
877 MOUSE_BUTTON3DOWN,
878 MOUSE_BUTTON2DOWN,
879 MOUSE_BUTTON1DOWN,
880 MOUSE_BUTTON4DOWN,
881 MOUSE_BUTTON5DOWN,
882 MOUSE_BUTTON6DOWN,
883 MOUSE_BUTTON7DOWN,
884 };
885 /* for PS/2 VersaPad */
886 static int butmapversaps2[8] = { /* VersaPad */
887 0,
888 MOUSE_BUTTON3DOWN,
889 0,
890 MOUSE_BUTTON3DOWN,
891 MOUSE_BUTTON1DOWN,
892 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
893 MOUSE_BUTTON1DOWN,
894 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
895 };
896 static int pBufP = 0;
897 static unsigned char pBuf[8];
898
899 debug("received char 0x%x", (int) rBuf);
900
901 /*
902 * Hack for resyncing: We check here for a package that is:
903 * a) illegal (detected by wrong data-package header)
904 * b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
905 * c) bad header-package
906 *
907 * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
908 * -128 are allowed, but since they are very seldom we can easily
909 * use them as package-header with no button pressed.
910 * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
911 * 0x80 is not valid as a header byte. For a PS/2 mouse we skip
912 * checking data bytes.
913 * For resyncing a PS/2 mouse we require the two most significant
914 * bits in the header byte to be 0. These are the overflow bits,
915 * and in case of an overflow we actually lose sync. Overflows
916 * are very rare, however, and we quickly gain sync again after
917 * an overflow condition. This is the best we can do. (Actually,
918 * we could use bit 0x08 in the header byte for resyncing, since
919 * that bit is supposed to be always on, but nobody told
920 * Microsoft...)
921 */
922
923 if (pBufP != 0 && ((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80)) {
924 pBufP = 0; /* skip package */
925 }
926 if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
927 return 0;
928
929 /* is there an extra data byte? */
930 if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1]) {
931 /*
932 * Hack for Logitech MouseMan Mouse - Middle button
933 *
934 * Unfortunately this mouse has variable length packets: the standard
935 * Microsoft 3 byte packet plus an optional 4th byte whenever the
936 * middle button status changes.
937 *
938 * We have already processed the standard packet with the movement
939 * and button info. Now post an event message with the old status
940 * of the left and right buttons and the updated middle button.
941 */
942
943 /*
944 * Even worse, different MouseMen and TrackMen differ in the 4th
945 * byte: some will send 0x00/0x20, others 0x01/0x21, or even
946 * 0x02/0x22, so I have to strip off the lower bits.
947 *
948 * [JCH-96/01/21]
949 * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
950 * and it is activated by tapping the glidepad with the finger! 8^)
951 * We map it to bit bit3, and the reverse map in xf86Events just has
952 * to be extended so that it is identified as Button 4. The lower
953 * half of the reverse-map may remain unchanged.
954 */
955
956 /*
957 * [KY-97/08/03]
958 * Receive the fourth byte only when preceding three bytes have
959 * been detected (pBufP >= cur_proto[4]). In the previous
960 * versions, the test was pBufP == 0; thus, we may have mistakingly
961 * received a byte even if we didn't see anything preceding
962 * the byte.
963 */
964
965 if ((rBuf & cur_proto[5]) != cur_proto[6]) {
966 pBufP = 0;
967 return 0;
968 }
969 switch (mouse.proto) {
970
971 /*
972 * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
973 * always send the fourth byte, whereas the fourth byte is
974 * optional for GlidePoint and ThinkingMouse. The fourth byte
975 * is also optional for MouseMan+ and FirstMouse+ in their
976 * native mode. It is always sent if they are in the IntelliMouse
977 * compatible mode.
978 */
979 case P_IMSERIAL: /* IntelliMouse, NetMouse, Mie Mouse,
980 * MouseMan+ */
981 act->dx = act->dy = 0;
982 act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
983 act->obutton = act->button;
984 act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
985 | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
986 break;
987
988 default:
989 act->dx = act->dy = act->dz = 0;
990 act->obutton = act->button;
991 act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
992 | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
993 break;
994 }
995
996 act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
997 | (act->obutton ^ act->button);
998 pBufP = 0;
999 return act->flags;
1000 }
1001 if (pBufP >= cur_proto[4])
1002 pBufP = 0;
1003 pBuf[pBufP++] = rBuf;
1004 if (pBufP != cur_proto[4])
1005 return 0;
1006
1007 /*
1008 * assembly full package
1009 */
1010
1011 debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
1012 cur_proto[4],
1013 pBuf[0], pBuf[1], pBuf[2], pBuf[3],
1014 pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
1015
1016 act->dz = 0;
1017 act->obutton = act->button;
1018 switch (mouse.proto) {
1019 case P_MS: /* Microsoft */
1020 case P_LOGIMAN: /* MouseMan/TrackMan */
1021 case P_GLIDEPOINT: /* GlidePoint */
1022 case P_THINKING: /* ThinkingMouse */
1023 case P_IMSERIAL: /* IntelliMouse, NetMouse, Mie Mouse,
1024 * MouseMan+ */
1025 act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
1026 | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1027 act->dx = (char) (((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1028 act->dy = (char) (((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1029 break;
1030
1031 case P_MSC: /* MouseSystems Corp */
1032 act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1033 act->dx = (char) (pBuf[1]) + (char) (pBuf[3]);
1034 act->dy = -((char) (pBuf[2]) + (char) (pBuf[4]));
1035 break;
1036
1037 case P_MMHIT: /* MM HitTablet */
1038 act->button = butmaphit[pBuf[0] & 0x07];
1039 act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : -pBuf[1];
1040 act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? -pBuf[2] : pBuf[2];
1041 break;
1042
1043 case P_MM: /* MM Series */
1044 case P_LOGI: /* Logitech Mice */
1045 act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
1046 act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : -pBuf[1];
1047 act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? -pBuf[2] : pBuf[2];
1048 break;
1049
1050 /*
1051 * XXX removed the code for BusMouse and PS/2 protocols which
1052 * are now handled by wsmouse compatible mouse drivers XXX
1053 */
1054
1055 default:
1056 return 0;
1057 }
1058
1059 /*
1060 * We don't reset pBufP here yet, as there may be an additional data
1061 * byte in some protocols. See above.
1062 */
1063 /* has something changed? */
1064 act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0) |
1065 (act->obutton ^ act->button);
1066 return act->flags;
1067 }
1068