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