xref: /freebsd/usr.sbin/moused/moused.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
1 /**
2  ** Copyright (c) 1995 Michael Smith, All rights reserved.
3  **
4  ** Redistribution and use in source and binary forms, with or without
5  ** modification, are permitted provided that the following conditions
6  ** are met:
7  ** 1. Redistributions of source code must retain the above copyright
8  **    notice, this list of conditions and the following disclaimer as
9  **    the first lines of this file unmodified.
10  ** 2. Redistributions in binary form must reproduce the above copyright
11  **    notice, this list of conditions and the following disclaimer in the
12  **    documentation and/or other materials provided with the distribution.
13  ** 3. All advertising materials mentioning features or use of this software
14  **    must display the following acknowledgment:
15  **      This product includes software developed by Michael Smith.
16  ** 4. The name of the author may not be used to endorse or promote products
17  **    derived from this software without specific prior written permission.
18  **
19  **
20  ** THIS SOFTWARE IS PROVIDED BY Michael Smith ``AS IS'' AND ANY
21  ** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith BE LIABLE FOR
24  ** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27  ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29  ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  ** EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  **
32  **/
33 
34 /**
35  ** MOUSED.C
36  **
37  ** Mouse daemon : listens to a serial port, the bus mouse interface, or
38  ** the PS/2 mouse port for mouse data stream, interprets data and passes
39  ** ioctls off to the console driver.
40  **
41  ** The mouse interface functions are derived closely from the mouse
42  ** handler in the XFree86 X server.  Many thanks to the XFree86 people
43  ** for their great work!
44  **
45  **/
46 
47 #include <sys/cdefs.h>
48 __FBSDID("$FreeBSD$");
49 
50 #include <sys/param.h>
51 #include <sys/consio.h>
52 #include <sys/linker.h>
53 #include <sys/module.h>
54 #include <sys/mouse.h>
55 #include <sys/socket.h>
56 #include <sys/stat.h>
57 #include <sys/time.h>
58 #include <sys/un.h>
59 
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <limits.h>
65 #include <setjmp.h>
66 #include <signal.h>
67 #include <stdarg.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <syslog.h>
72 #include <termios.h>
73 #include <unistd.h>
74 
75 #define MAX_CLICKTHRESHOLD	2000	/* 2 seconds */
76 #define MAX_BUTTON2TIMEOUT	2000	/* 2 seconds */
77 #define DFLT_CLICKTHRESHOLD	 500	/* 0.5 second */
78 #define DFLT_BUTTON2TIMEOUT	 100	/* 0.1 second */
79 
80 /* Abort 3-button emulation delay after this many movement events. */
81 #define BUTTON2_MAXMOVE	3
82 
83 #define TRUE		1
84 #define FALSE		0
85 
86 #define MOUSE_XAXIS	(-1)
87 #define MOUSE_YAXIS	(-2)
88 
89 /* Logitech PS2++ protocol */
90 #define MOUSE_PS2PLUS_CHECKBITS(b)	\
91 			((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
92 #define MOUSE_PS2PLUS_PACKET_TYPE(b)	\
93 			(((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
94 
95 #define	ChordMiddle	0x0001
96 #define Emulate3Button	0x0002
97 #define ClearDTR	0x0004
98 #define ClearRTS	0x0008
99 #define NoPnP		0x0010
100 
101 #define ID_NONE		0
102 #define ID_PORT		1
103 #define ID_IF		2
104 #define ID_TYPE	4
105 #define ID_MODEL	8
106 #define ID_ALL		(ID_PORT | ID_IF | ID_TYPE | ID_MODEL)
107 
108 #define debug(fmt, args...) do {				\
109 	if (debug && nodaemon)					\
110 		warnx(fmt, ##args);				\
111 } while (0)
112 
113 #define logerr(e, fmt, args...) do {				\
114 	log_or_warn(LOG_DAEMON | LOG_ERR, errno, fmt, ##args);	\
115 	exit(e);						\
116 } while (0)
117 
118 #define logerrx(e, fmt, args...) do {				\
119 	log_or_warn(LOG_DAEMON | LOG_ERR, 0, fmt, ##args);	\
120 	exit(e);						\
121 } while (0)
122 
123 #define logwarn(fmt, args...)					\
124 	log_or_warn(LOG_DAEMON | LOG_WARNING, errno, fmt, ##args)
125 
126 #define logwarnx(fmt, args...)					\
127 	log_or_warn(LOG_DAEMON | LOG_WARNING, 0, fmt, ##args)
128 
129 /* structures */
130 
131 /* symbol table entry */
132 typedef struct {
133     char *name;
134     int val;
135     int val2;
136 } symtab_t;
137 
138 /* serial PnP ID string */
139 typedef struct {
140     int revision;	/* PnP revision, 100 for 1.00 */
141     char *eisaid;	/* EISA ID including mfr ID and product ID */
142     char *serial;	/* serial No, optional */
143     char *class;	/* device class, optional */
144     char *compat;	/* list of compatible drivers, optional */
145     char *description;	/* product description, optional */
146     int neisaid;	/* length of the above fields... */
147     int nserial;
148     int nclass;
149     int ncompat;
150     int ndescription;
151 } pnpid_t;
152 
153 /* global variables */
154 
155 int	debug = 0;
156 int	nodaemon = FALSE;
157 int	background = FALSE;
158 int	identify = ID_NONE;
159 int	extioctl = FALSE;
160 char	*pidfile = "/var/run/moused.pid";
161 
162 /* local variables */
163 
164 /* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
165 static symtab_t rifs[] = {
166     { "serial",		MOUSE_IF_SERIAL },
167     { "bus",		MOUSE_IF_BUS },
168     { "inport",		MOUSE_IF_INPORT },
169     { "ps/2",		MOUSE_IF_PS2 },
170     { "sysmouse",	MOUSE_IF_SYSMOUSE },
171     { "usb",		MOUSE_IF_USB },
172     { NULL,		MOUSE_IF_UNKNOWN },
173 };
174 
175 /* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
176 static char *rnames[] = {
177     "microsoft",
178     "mousesystems",
179     "logitech",
180     "mmseries",
181     "mouseman",
182     "busmouse",
183     "inportmouse",
184     "ps/2",
185     "mmhitab",
186     "glidepoint",
187     "intellimouse",
188     "thinkingmouse",
189     "sysmouse",
190     "x10mouseremote",
191     "kidspad",
192     "versapad",
193     "jogdial",
194 #if notyet
195     "mariqua",
196 #endif
197     NULL
198 };
199 
200 /* models */
201 static symtab_t	rmodels[] = {
202     { "NetScroll",		MOUSE_MODEL_NETSCROLL },
203     { "NetMouse/NetScroll Optical", MOUSE_MODEL_NET },
204     { "GlidePoint",		MOUSE_MODEL_GLIDEPOINT },
205     { "ThinkingMouse",		MOUSE_MODEL_THINK },
206     { "IntelliMouse",		MOUSE_MODEL_INTELLI },
207     { "EasyScroll/SmartScroll",	MOUSE_MODEL_EASYSCROLL },
208     { "MouseMan+",		MOUSE_MODEL_MOUSEMANPLUS },
209     { "Kidspad",		MOUSE_MODEL_KIDSPAD },
210     { "VersaPad",		MOUSE_MODEL_VERSAPAD },
211     { "IntelliMouse Explorer",	MOUSE_MODEL_EXPLORER },
212     { "4D Mouse",		MOUSE_MODEL_4D },
213     { "4D+ Mouse",		MOUSE_MODEL_4DPLUS },
214     { "generic",		MOUSE_MODEL_GENERIC },
215     { NULL,			MOUSE_MODEL_UNKNOWN },
216 };
217 
218 /* PnP EISA/product IDs */
219 static symtab_t pnpprod[] = {
220     /* Kensignton ThinkingMouse */
221     { "KML0001",	MOUSE_PROTO_THINK,	MOUSE_MODEL_THINK },
222     /* MS IntelliMouse */
223     { "MSH0001",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
224     /* MS IntelliMouse TrackBall */
225     { "MSH0004",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
226     /* Tremon Wheel Mouse MUSD */
227     { "HTK0001",        MOUSE_PROTO_INTELLI,    MOUSE_MODEL_INTELLI },
228     /* Genius PnP Mouse */
229     { "KYE0001",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
230     /* MouseSystems SmartScroll Mouse (OEM from Genius?) */
231     { "KYE0002",	MOUSE_PROTO_MS,		MOUSE_MODEL_EASYSCROLL },
232     /* Genius NetMouse */
233     { "KYE0003",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_NET },
234     /* Genius Kidspad, Easypad and other tablets */
235     { "KYE0005",	MOUSE_PROTO_KIDSPAD,	MOUSE_MODEL_KIDSPAD },
236     /* Genius EZScroll */
237     { "KYEEZ00",	MOUSE_PROTO_MS,		MOUSE_MODEL_EASYSCROLL },
238     /* Logitech Cordless MouseMan Wheel */
239     { "LGI8033",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
240     /* Logitech MouseMan (new 4 button model) */
241     { "LGI800C",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
242     /* Logitech MouseMan+ */
243     { "LGI8050",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
244     /* Logitech FirstMouse+ */
245     { "LGI8051",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
246     /* Logitech serial */
247     { "LGI8001",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
248     /* A4 Tech 4D/4D+ Mouse */
249     { "A4W0005",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_4D },
250     /* 8D Scroll Mouse */
251     { "PEC9802",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
252     /* Mitsumi Wireless Scroll Mouse */
253     { "MTM6401",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
254 
255     /* MS bus */
256     { "PNP0F00",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
257     /* MS serial */
258     { "PNP0F01",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
259     /* MS InPort */
260     { "PNP0F02",	MOUSE_PROTO_INPORT,	MOUSE_MODEL_GENERIC },
261     /* MS PS/2 */
262     { "PNP0F03",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
263     /*
264      * EzScroll returns PNP0F04 in the compatible device field; but it
265      * doesn't look compatible... XXX
266      */
267     /* MouseSystems */
268     { "PNP0F04",	MOUSE_PROTO_MSC,	MOUSE_MODEL_GENERIC },
269     /* MouseSystems */
270     { "PNP0F05",	MOUSE_PROTO_MSC,	MOUSE_MODEL_GENERIC },
271 #if notyet
272     /* Genius Mouse */
273     { "PNP0F06",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
274     /* Genius Mouse */
275     { "PNP0F07",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
276 #endif
277     /* Logitech serial */
278     { "PNP0F08",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
279     /* MS BallPoint serial */
280     { "PNP0F09",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
281     /* MS PnP serial */
282     { "PNP0F0A",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
283     /* MS PnP BallPoint serial */
284     { "PNP0F0B",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
285     /* MS serial comatible */
286     { "PNP0F0C",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
287     /* MS InPort comatible */
288     { "PNP0F0D",	MOUSE_PROTO_INPORT,	MOUSE_MODEL_GENERIC },
289     /* MS PS/2 comatible */
290     { "PNP0F0E",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
291     /* MS BallPoint comatible */
292     { "PNP0F0F",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
293 #if notyet
294     /* TI QuickPort */
295     { "PNP0F10",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
296 #endif
297     /* MS bus comatible */
298     { "PNP0F11",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
299     /* Logitech PS/2 */
300     { "PNP0F12",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
301     /* PS/2 */
302     { "PNP0F13",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
303 #if notyet
304     /* MS Kids Mouse */
305     { "PNP0F14",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
306 #endif
307     /* Logitech bus */
308     { "PNP0F15",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
309 #if notyet
310     /* Logitech SWIFT */
311     { "PNP0F16",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
312 #endif
313     /* Logitech serial compat */
314     { "PNP0F17",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
315     /* Logitech bus compatible */
316     { "PNP0F18",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
317     /* Logitech PS/2 compatible */
318     { "PNP0F19",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
319 #if notyet
320     /* Logitech SWIFT compatible */
321     { "PNP0F1A",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
322     /* HP Omnibook */
323     { "PNP0F1B",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
324     /* Compaq LTE TrackBall PS/2 */
325     { "PNP0F1C",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
326     /* Compaq LTE TrackBall serial */
327     { "PNP0F1D",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
328     /* MS Kidts Trackball */
329     { "PNP0F1E",	MOUSE_PROTO_XXX,	MOUSE_MODEL_GENERIC },
330 #endif
331     /* Interlink VersaPad */
332     { "LNK0001",	MOUSE_PROTO_VERSAPAD,	MOUSE_MODEL_VERSAPAD },
333 
334     { NULL,		MOUSE_PROTO_UNKNOWN,	MOUSE_MODEL_GENERIC },
335 };
336 
337 /* the table must be ordered by MOUSE_PROTO_XXX in mouse.h */
338 static unsigned short rodentcflags[] =
339 {
340     (CS7	           | CREAD | CLOCAL | HUPCL),	/* MicroSoft */
341     (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL),	/* MouseSystems */
342     (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL),	/* Logitech */
343     (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL),	/* MMSeries */
344     (CS7		   | CREAD | CLOCAL | HUPCL),	/* MouseMan */
345     0,							/* Bus */
346     0,							/* InPort */
347     0,							/* PS/2 */
348     (CS8		   | CREAD | CLOCAL | HUPCL),	/* MM HitTablet */
349     (CS7	           | CREAD | CLOCAL | HUPCL),	/* GlidePoint */
350     (CS7                   | CREAD | CLOCAL | HUPCL),	/* IntelliMouse */
351     (CS7                   | CREAD | CLOCAL | HUPCL),	/* Thinking Mouse */
352     (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL),	/* sysmouse */
353     (CS7	           | CREAD | CLOCAL | HUPCL),	/* X10 MouseRemote */
354     (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL),	/* kidspad etc. */
355     (CS8		   | CREAD | CLOCAL | HUPCL),	/* VersaPad */
356     0,							/* JogDial */
357 #if notyet
358     (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL),	/* Mariqua */
359 #endif
360 };
361 
362 static struct rodentparam {
363     int flags;
364     char *portname;		/* /dev/XXX */
365     int rtype;			/* MOUSE_PROTO_XXX */
366     int level;			/* operation level: 0 or greater */
367     int baudrate;
368     int rate;			/* report rate */
369     int resolution;		/* MOUSE_RES_XXX or a positive number */
370     int zmap[4];		/* MOUSE_{X|Y}AXIS or a button number */
371     int wmode;			/* wheel mode button number */
372     int mfd;			/* mouse file descriptor */
373     int cfd;			/* /dev/consolectl file descriptor */
374     int mremsfd;		/* mouse remote server file descriptor */
375     int mremcfd;		/* mouse remote client file descriptor */
376     long clickthreshold;	/* double click speed in msec */
377     long button2timeout;	/* 3 button emulation timeout */
378     mousehw_t hw;		/* mouse device hardware information */
379     mousemode_t mode;		/* protocol information */
380     float accelx;		/* Acceleration in the X axis */
381     float accely;		/* Acceleration in the Y axis */
382 } rodent = {
383     flags : 0,
384     portname : NULL,
385     rtype : MOUSE_PROTO_UNKNOWN,
386     level : -1,
387     baudrate : 1200,
388     rate : 0,
389     resolution : MOUSE_RES_UNKNOWN,
390     zmap: { 0, 0, 0, 0 },
391     wmode: 0,
392     mfd : -1,
393     cfd : -1,
394     mremsfd : -1,
395     mremcfd : -1,
396     clickthreshold : DFLT_CLICKTHRESHOLD,
397     button2timeout : DFLT_BUTTON2TIMEOUT,
398     accelx : 1.0,
399     accely : 1.0,
400 };
401 
402 /* button status */
403 struct button_state {
404     int count;		/* 0: up, 1: single click, 2: double click,... */
405     struct timeval tv;	/* timestamp on the last button event */
406 };
407 static struct button_state	bstate[MOUSE_MAXBUTTON]; /* button state */
408 static struct button_state	*mstate[MOUSE_MAXBUTTON];/* mapped button st.*/
409 static struct button_state	zstate[4];		 /* Z/W axis state */
410 
411 /* state machine for 3 button emulation */
412 
413 #define S0	0	/* start */
414 #define S1	1	/* button 1 delayed down */
415 #define S2	2	/* button 3 delayed down */
416 #define S3	3	/* both buttons down -> button 2 down */
417 #define S4	4	/* button 1 delayed up */
418 #define S5	5	/* button 1 down */
419 #define S6	6	/* button 3 down */
420 #define S7	7	/* both buttons down */
421 #define S8	8	/* button 3 delayed up */
422 #define S9	9	/* button 1 or 3 up after S3 */
423 
424 #define A(b1, b3)	(((b1) ? 2 : 0) | ((b3) ? 1 : 0))
425 #define A_TIMEOUT	4
426 #define S_DELAYED(st)	(states[st].s[A_TIMEOUT] != (st))
427 
428 static struct {
429     int s[A_TIMEOUT + 1];
430     int buttons;
431     int mask;
432     int timeout;
433 } states[10] = {
434     /* S0 */
435     { { S0, S2, S1, S3, S0 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
436     /* S1 */
437     { { S4, S2, S1, S3, S5 }, 0, ~MOUSE_BUTTON1DOWN, FALSE },
438     /* S2 */
439     { { S8, S2, S1, S3, S6 }, 0, ~MOUSE_BUTTON3DOWN, FALSE },
440     /* S3 */
441     { { S0, S9, S9, S3, S3 }, MOUSE_BUTTON2DOWN, ~0, FALSE },
442     /* S4 */
443     { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON1DOWN, ~0, TRUE },
444     /* S5 */
445     { { S0, S2, S5, S7, S5 }, MOUSE_BUTTON1DOWN, ~0, FALSE },
446     /* S6 */
447     { { S0, S6, S1, S7, S6 }, MOUSE_BUTTON3DOWN, ~0, FALSE },
448     /* S7 */
449     { { S0, S6, S5, S7, S7 }, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, ~0, FALSE },
450     /* S8 */
451     { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON3DOWN, ~0, TRUE },
452     /* S9 */
453     { { S0, S9, S9, S3, S9 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
454 };
455 static int		mouse_button_state;
456 static struct timeval	mouse_button_state_tv;
457 static int		mouse_move_delayed;
458 
459 static jmp_buf env;
460 
461 /* function prototypes */
462 
463 static void	moused(void);
464 static void	hup(int sig);
465 static void	cleanup(int sig);
466 static void	usage(void);
467 static void	log_or_warn(int log_pri, int errnum, const char *fmt, ...)
468 		    __printflike(3, 4);
469 
470 static int	r_identify(void);
471 static char	*r_if(int type);
472 static char	*r_name(int type);
473 static char	*r_model(int model);
474 static void	r_init(void);
475 static int	r_protocol(u_char b, mousestatus_t *act);
476 static int	r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans);
477 static int	r_installmap(char *arg);
478 static void	r_map(mousestatus_t *act1, mousestatus_t *act2);
479 static void	r_timestamp(mousestatus_t *act);
480 static int	r_timeout(void);
481 static void	r_click(mousestatus_t *act);
482 static void	setmousespeed(int old, int new, unsigned cflag);
483 
484 static int	pnpwakeup1(void);
485 static int	pnpwakeup2(void);
486 static int	pnpgets(char *buf);
487 static int	pnpparse(pnpid_t *id, char *buf, int len);
488 static symtab_t	*pnpproto(pnpid_t *id);
489 
490 static symtab_t	*gettoken(symtab_t *tab, char *s, int len);
491 static char	*gettokenname(symtab_t *tab, int val);
492 
493 static void	mremote_serversetup();
494 static void	mremote_clientchg(int add);
495 
496 static int	kidspad(u_char rxc, mousestatus_t *act);
497 
498 static int	usbmodule(void);
499 
500 int
501 main(int argc, char *argv[])
502 {
503     int c;
504     int	i;
505     int	j;
506     int retry;
507 
508     for (i = 0; i < MOUSE_MAXBUTTON; ++i)
509 	mstate[i] = &bstate[i];
510 
511     while ((c = getopt(argc, argv, "3C:DE:F:I:PRS:a:cdfhi:l:m:p:r:st:w:z:")) != -1)
512 	switch(c) {
513 
514 	case '3':
515 	    rodent.flags |= Emulate3Button;
516 	    break;
517 
518 	case 'E':
519 	    rodent.button2timeout = atoi(optarg);
520 	    if ((rodent.button2timeout < 0) ||
521 		(rodent.button2timeout > MAX_BUTTON2TIMEOUT)) {
522 		warnx("invalid argument `%s'", optarg);
523 		usage();
524 	    }
525 	    break;
526 
527 	case 'a':
528 	    i = sscanf(optarg, "%f,%f", &rodent.accelx, &rodent.accely);
529 	    if (i == 0) {
530 		warnx("invalid acceleration argument '%s'", optarg);
531 		usage();
532 	    }
533 
534 	    if (i == 1)
535 		rodent.accely = rodent.accelx;
536 
537 	    break;
538 
539 	case 'c':
540 	    rodent.flags |= ChordMiddle;
541 	    break;
542 
543 	case 'd':
544 	    ++debug;
545 	    break;
546 
547 	case 'f':
548 	    nodaemon = TRUE;
549 	    break;
550 
551 	case 'i':
552 	    if (strcmp(optarg, "all") == 0)
553 		identify = ID_ALL;
554 	    else if (strcmp(optarg, "port") == 0)
555 		identify = ID_PORT;
556 	    else if (strcmp(optarg, "if") == 0)
557 		identify = ID_IF;
558 	    else if (strcmp(optarg, "type") == 0)
559 		identify = ID_TYPE;
560 	    else if (strcmp(optarg, "model") == 0)
561 		identify = ID_MODEL;
562 	    else {
563 		warnx("invalid argument `%s'", optarg);
564 		usage();
565 	    }
566 	    nodaemon = TRUE;
567 	    break;
568 
569 	case 'l':
570 	    rodent.level = atoi(optarg);
571 	    if ((rodent.level < 0) || (rodent.level > 4)) {
572 		warnx("invalid argument `%s'", optarg);
573 		usage();
574 	    }
575 	    break;
576 
577 	case 'm':
578 	    if (!r_installmap(optarg)) {
579 		warnx("invalid argument `%s'", optarg);
580 		usage();
581 	    }
582 	    break;
583 
584 	case 'p':
585 	    rodent.portname = optarg;
586 	    break;
587 
588 	case 'r':
589 	    if (strcmp(optarg, "high") == 0)
590 		rodent.resolution = MOUSE_RES_HIGH;
591 	    else if (strcmp(optarg, "medium-high") == 0)
592 		rodent.resolution = MOUSE_RES_HIGH;
593 	    else if (strcmp(optarg, "medium-low") == 0)
594 		rodent.resolution = MOUSE_RES_MEDIUMLOW;
595 	    else if (strcmp(optarg, "low") == 0)
596 		rodent.resolution = MOUSE_RES_LOW;
597 	    else if (strcmp(optarg, "default") == 0)
598 		rodent.resolution = MOUSE_RES_DEFAULT;
599 	    else {
600 		rodent.resolution = atoi(optarg);
601 		if (rodent.resolution <= 0) {
602 		    warnx("invalid argument `%s'", optarg);
603 		    usage();
604 		}
605 	    }
606 	    break;
607 
608 	case 's':
609 	    rodent.baudrate = 9600;
610 	    break;
611 
612 	case 'w':
613 	    i = atoi(optarg);
614 	    if ((i <= 0) || (i > MOUSE_MAXBUTTON)) {
615 		warnx("invalid argument `%s'", optarg);
616 		usage();
617 	    }
618 	    rodent.wmode = 1 << (i - 1);
619 	    break;
620 
621 	case 'z':
622 	    if (strcmp(optarg, "x") == 0)
623 		rodent.zmap[0] = MOUSE_XAXIS;
624 	    else if (strcmp(optarg, "y") == 0)
625 		rodent.zmap[0] = MOUSE_YAXIS;
626 	    else {
627 		i = atoi(optarg);
628 		/*
629 		 * Use button i for negative Z axis movement and
630 		 * button (i + 1) for positive Z axis movement.
631 		 */
632 		if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
633 		    warnx("invalid argument `%s'", optarg);
634 		    usage();
635 		}
636 		rodent.zmap[0] = i;
637 		rodent.zmap[1] = i + 1;
638 		debug("optind: %d, optarg: '%s'", optind, optarg);
639 		for (j = 1; j < 4; ++j) {
640 		    if ((optind >= argc) || !isdigit(*argv[optind]))
641 			break;
642 		    i = atoi(argv[optind]);
643 		    if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
644 			warnx("invalid argument `%s'", argv[optind]);
645 			usage();
646 		    }
647 		    rodent.zmap[j] = i;
648 		    ++optind;
649 		}
650 		if ((rodent.zmap[2] != 0) && (rodent.zmap[3] == 0))
651 		    rodent.zmap[3] = rodent.zmap[2] + 1;
652 	    }
653 	    break;
654 
655 	case 'C':
656 	    rodent.clickthreshold = atoi(optarg);
657 	    if ((rodent.clickthreshold < 0) ||
658 		(rodent.clickthreshold > MAX_CLICKTHRESHOLD)) {
659 		warnx("invalid argument `%s'", optarg);
660 		usage();
661 	    }
662 	    break;
663 
664 	case 'D':
665 	    rodent.flags |= ClearDTR;
666 	    break;
667 
668 	case 'F':
669 	    rodent.rate = atoi(optarg);
670 	    if (rodent.rate <= 0) {
671 		warnx("invalid argument `%s'", optarg);
672 		usage();
673 	    }
674 	    break;
675 
676 	case 'I':
677 	    pidfile = optarg;
678 	    break;
679 
680 	case 'P':
681 	    rodent.flags |= NoPnP;
682 	    break;
683 
684 	case 'R':
685 	    rodent.flags |= ClearRTS;
686 	    break;
687 
688 	case 'S':
689 	    rodent.baudrate = atoi(optarg);
690 	    if (rodent.baudrate <= 0) {
691 		warnx("invalid argument `%s'", optarg);
692 		usage();
693 	    }
694 	    debug("rodent baudrate %d", rodent.baudrate);
695 	    break;
696 
697 	case 't':
698 	    if (strcmp(optarg, "auto") == 0) {
699 		rodent.rtype = MOUSE_PROTO_UNKNOWN;
700 		rodent.flags &= ~NoPnP;
701 		rodent.level = -1;
702 		break;
703 	    }
704 	    for (i = 0; rnames[i]; i++)
705 		if (strcmp(optarg, rnames[i]) == 0) {
706 		    rodent.rtype = i;
707 		    rodent.flags |= NoPnP;
708 		    rodent.level = (i == MOUSE_PROTO_SYSMOUSE) ? 1 : 0;
709 		    break;
710 		}
711 	    if (rnames[i])
712 		break;
713 	    warnx("no such mouse type `%s'", optarg);
714 	    usage();
715 
716 	case 'h':
717 	case '?':
718 	default:
719 	    usage();
720 	}
721 
722     /* fix Z axis mapping */
723     for (i = 0; i < 4; ++i) {
724 	if (rodent.zmap[i] > 0) {
725 	    for (j = 0; j < MOUSE_MAXBUTTON; ++j) {
726 		if (mstate[j] == &bstate[rodent.zmap[i] - 1])
727 		    mstate[j] = &zstate[i];
728 	    }
729 	    rodent.zmap[i] = 1 << (rodent.zmap[i] - 1);
730 	}
731     }
732 
733     /* the default port name */
734     switch(rodent.rtype) {
735 
736     case MOUSE_PROTO_INPORT:
737 	/* INPORT and BUS are the same... */
738 	rodent.rtype = MOUSE_PROTO_BUS;
739 	/* FALLTHROUGH */
740     case MOUSE_PROTO_BUS:
741 	if (!rodent.portname)
742 	    rodent.portname = "/dev/mse0";
743 	break;
744 
745     case MOUSE_PROTO_PS2:
746 	if (!rodent.portname)
747 	    rodent.portname = "/dev/psm0";
748 	break;
749 
750     default:
751 	if (rodent.portname)
752 	    break;
753 	warnx("no port name specified");
754 	usage();
755     }
756 
757     retry = 1;
758     if (strncmp(rodent.portname, "/dev/ums", 8) == 0) {
759 	if (usbmodule() != 0)
760 	    retry = 5;
761     }
762 
763     for (;;) {
764 	if (setjmp(env) == 0) {
765 	    signal(SIGHUP, hup);
766 	    signal(SIGINT , cleanup);
767 	    signal(SIGQUIT, cleanup);
768 	    signal(SIGTERM, cleanup);
769 	    for (i = 0; i < retry; ++i) {
770 		if (i > 0)
771 		    sleep(2);
772 		rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK);
773 		if (rodent.mfd != -1 || errno != ENOENT)
774 		    break;
775 	    }
776 	    if (rodent.mfd == -1)
777 		logerr(1, "unable to open %s", rodent.portname);
778 	    if (r_identify() == MOUSE_PROTO_UNKNOWN) {
779 		logwarnx("cannot determine mouse type on %s", rodent.portname);
780 		close(rodent.mfd);
781 		rodent.mfd = -1;
782 	    }
783 
784 	    /* print some information */
785 	    if (identify != ID_NONE) {
786 		if (identify == ID_ALL)
787 		    printf("%s %s %s %s\n",
788 			rodent.portname, r_if(rodent.hw.iftype),
789 			r_name(rodent.rtype), r_model(rodent.hw.model));
790 		else if (identify & ID_PORT)
791 		    printf("%s\n", rodent.portname);
792 		else if (identify & ID_IF)
793 		    printf("%s\n", r_if(rodent.hw.iftype));
794 		else if (identify & ID_TYPE)
795 		    printf("%s\n", r_name(rodent.rtype));
796 		else if (identify & ID_MODEL)
797 		    printf("%s\n", r_model(rodent.hw.model));
798 		exit(0);
799 	    } else {
800 		debug("port: %s  interface: %s  type: %s  model: %s",
801 		    rodent.portname, r_if(rodent.hw.iftype),
802 		    r_name(rodent.rtype), r_model(rodent.hw.model));
803 	    }
804 
805 	    if (rodent.mfd == -1) {
806 		/*
807 		 * We cannot continue because of error.  Exit if the
808 		 * program has not become a daemon.  Otherwise, block
809 		 * until the the user corrects the problem and issues SIGHUP.
810 		 */
811 		if (!background)
812 		    exit(1);
813 		sigpause(0);
814 	    }
815 
816 	    r_init();			/* call init function */
817 	    moused();
818 	}
819 
820 	if (rodent.mfd != -1)
821 	    close(rodent.mfd);
822 	if (rodent.cfd != -1)
823 	    close(rodent.cfd);
824 	rodent.mfd = rodent.cfd = -1;
825     }
826     /* NOT REACHED */
827 
828     exit(0);
829 }
830 
831 static int
832 usbmodule(void)
833 {
834     struct kld_file_stat fstat;
835     struct module_stat mstat;
836     int fileid, modid;
837     int loaded;
838 
839     for (loaded = 0, fileid = kldnext(0); !loaded && fileid > 0;
840 	 fileid = kldnext(fileid)) {
841 	fstat.version = sizeof(fstat);
842 	if (kldstat(fileid, &fstat) < 0)
843 	    continue;
844 	if (strncmp(fstat.name, "uhub/ums", 8) == 0) {
845 	    loaded = 1;
846 	    break;
847 	}
848 	for (modid = kldfirstmod(fileid); modid > 0;
849 	     modid = modfnext(modid)) {
850 	    mstat.version = sizeof(mstat);
851 	    if (modstat(modid, &mstat) < 0)
852 		continue;
853 	    if (strncmp(mstat.name, "uhub/ums", 8) == 0) {
854 		loaded = 1;
855 		break;
856 	    }
857 	}
858     }
859     if (!loaded) {
860 	if (kldload("ums") != -1)
861 	    return 1;
862 	if (errno != EEXIST)
863 	    logerr(1, "unable to load USB mouse driver");
864     }
865     return 0;
866 }
867 
868 static void
869 moused(void)
870 {
871     struct mouse_info mouse;
872     mousestatus_t action0;		/* original mouse action */
873     mousestatus_t action;		/* interrim buffer */
874     mousestatus_t action2;		/* mapped action */
875     struct timeval timeout;
876     fd_set fds;
877     u_char b;
878     FILE *fp;
879     int flags;
880     int c;
881     int i;
882 
883     if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
884 	logerr(1, "cannot open /dev/consolectl");
885 
886     if (!nodaemon && !background) {
887 	if (daemon(0, 0)) {
888 	    logerr(1, "failed to become a daemon");
889 	} else {
890 	    background = TRUE;
891 	    fp = fopen(pidfile, "w");
892 	    if (fp != NULL) {
893 		fprintf(fp, "%d\n", getpid());
894 		fclose(fp);
895 	    }
896 	}
897     }
898 
899     /* clear mouse data */
900     bzero(&action0, sizeof(action0));
901     bzero(&action, sizeof(action));
902     bzero(&action2, sizeof(action2));
903     bzero(&mouse, sizeof(mouse));
904     mouse_button_state = S0;
905     gettimeofday(&mouse_button_state_tv, NULL);
906     mouse_move_delayed = 0;
907     for (i = 0; i < MOUSE_MAXBUTTON; ++i) {
908 	bstate[i].count = 0;
909 	bstate[i].tv = mouse_button_state_tv;
910     }
911     for (i = 0; i < sizeof(zstate)/sizeof(zstate[0]); ++i) {
912 	zstate[i].count = 0;
913 	zstate[i].tv = mouse_button_state_tv;
914     }
915 
916     /* choose which ioctl command to use */
917     mouse.operation = MOUSE_MOTION_EVENT;
918     extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
919 
920     /* process mouse data */
921     timeout.tv_sec = 0;
922     timeout.tv_usec = 20000;		/* 20 msec */
923     for (;;) {
924 
925 	FD_ZERO(&fds);
926 	FD_SET(rodent.mfd, &fds);
927 	if (rodent.mremsfd >= 0)
928 	    FD_SET(rodent.mremsfd, &fds);
929 	if (rodent.mremcfd >= 0)
930 	    FD_SET(rodent.mremcfd, &fds);
931 
932 	c = select(FD_SETSIZE, &fds, NULL, NULL,
933 		   (rodent.flags & Emulate3Button) ? &timeout : NULL);
934 	if (c < 0) {                    /* error */
935 	    logwarn("failed to read from mouse");
936 	    continue;
937 	} else if (c == 0) {            /* timeout */
938 	    /* assert(rodent.flags & Emulate3Button) */
939 	    action0.button = action0.obutton;
940 	    action0.dx = action0.dy = action0.dz = 0;
941 	    action0.flags = flags = 0;
942 	    if (r_timeout() && r_statetrans(&action0, &action, A_TIMEOUT)) {
943 		if (debug > 2)
944 		    debug("flags:%08x buttons:%08x obuttons:%08x",
945 			  action.flags, action.button, action.obutton);
946 	    } else {
947 		action0.obutton = action0.button;
948 		continue;
949 	    }
950 	} else {
951 	    /*  MouseRemote client connect/disconnect  */
952 	    if ((rodent.mremsfd >= 0) && FD_ISSET(rodent.mremsfd, &fds)) {
953 		mremote_clientchg(TRUE);
954 		continue;
955 	    }
956 	    if ((rodent.mremcfd >= 0) && FD_ISSET(rodent.mremcfd, &fds)) {
957 		mremote_clientchg(FALSE);
958 		continue;
959 	    }
960 	    /* mouse movement */
961 	    if (read(rodent.mfd, &b, 1) == -1) {
962 		if (errno == EWOULDBLOCK)
963 		    continue;
964 		else
965 		    return;
966 	    }
967 	    if ((flags = r_protocol(b, &action0)) == 0)
968 		continue;
969 	    r_timestamp(&action0);
970 	    r_statetrans(&action0, &action,
971 			 A(action0.button & MOUSE_BUTTON1DOWN,
972 			   action0.button & MOUSE_BUTTON3DOWN));
973 	    debug("flags:%08x buttons:%08x obuttons:%08x", action.flags,
974 		  action.button, action.obutton);
975 	}
976 	action0.obutton = action0.button;
977 	flags &= MOUSE_POSCHANGED;
978 	flags |= action.obutton ^ action.button;
979 	action.flags = flags;
980 
981 	if (flags) {			/* handler detected action */
982 	    r_map(&action, &action2);
983 	    debug("activity : buttons 0x%08x  dx %d  dy %d  dz %d",
984 		action2.button, action2.dx, action2.dy, action2.dz);
985 
986 	    if (extioctl) {
987 		r_click(&action2);
988 		if (action2.flags & MOUSE_POSCHANGED) {
989 		    mouse.operation = MOUSE_MOTION_EVENT;
990 		    mouse.u.data.buttons = action2.button;
991 		    mouse.u.data.x = action2.dx * rodent.accelx;
992 		    mouse.u.data.y = action2.dy * rodent.accely;
993 		    mouse.u.data.z = action2.dz;
994 		    if (debug < 2)
995 			ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
996 		}
997 	    } else {
998 		mouse.operation = MOUSE_ACTION;
999 		mouse.u.data.buttons = action2.button;
1000 		mouse.u.data.x = action2.dx * rodent.accelx;
1001 		mouse.u.data.y = action2.dy * rodent.accely;
1002 		mouse.u.data.z = action2.dz;
1003 		if (debug < 2)
1004 		    ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
1005 	    }
1006 
1007 	    /*
1008 	     * If the Z axis movement is mapped to an imaginary physical
1009 	     * button, we need to cook up a corresponding button `up' event
1010 	     * after sending a button `down' event.
1011 	     */
1012 	    if ((rodent.zmap[0] > 0) && (action.dz != 0)) {
1013 		action.obutton = action.button;
1014 		action.dx = action.dy = action.dz = 0;
1015 		r_map(&action, &action2);
1016 		debug("activity : buttons 0x%08x  dx %d  dy %d  dz %d",
1017 		    action2.button, action2.dx, action2.dy, action2.dz);
1018 
1019 		if (extioctl) {
1020 		    r_click(&action2);
1021 		} else {
1022 		    mouse.operation = MOUSE_ACTION;
1023 		    mouse.u.data.buttons = action2.button;
1024 		    mouse.u.data.x = mouse.u.data.y = mouse.u.data.z = 0;
1025 		    if (debug < 2)
1026 			ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
1027 		}
1028 	    }
1029 	}
1030     }
1031     /* NOT REACHED */
1032 }
1033 
1034 static void
1035 hup(int sig)
1036 {
1037     longjmp(env, 1);
1038 }
1039 
1040 static void
1041 cleanup(int sig)
1042 {
1043     if (rodent.rtype == MOUSE_PROTO_X10MOUSEREM)
1044 	unlink(_PATH_MOUSEREMOTE);
1045     exit(0);
1046 }
1047 
1048 /**
1049  ** usage
1050  **
1051  ** Complain, and free the CPU for more worthy tasks
1052  **/
1053 static void
1054 usage(void)
1055 {
1056     fprintf(stderr, "%s\n%s\n%s\n%s\n",
1057 	"usage: moused [-DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
1058 	"              [-a X [,Y]] [-C threshold] [-m N=M] [-w N] [-z N]",
1059 	"              [-t <mousetype>] [-3 [-E timeout]] -p <port>",
1060 	"       moused [-d] -i <port|if|type|model|all> -p <port>");
1061     exit(1);
1062 }
1063 
1064 /*
1065  * Output an error message to syslog or stderr as appropriate. If
1066  * `errnum' is non-zero, append its string form to the message.
1067  */
1068 static void
1069 log_or_warn(int log_pri, int errnum, const char *fmt, ...)
1070 {
1071 	va_list ap;
1072 	char buf[256];
1073 
1074 	va_start(ap, fmt);
1075 	vsnprintf(buf, sizeof(buf), fmt, ap);
1076 	va_end(ap);
1077 	if (errnum) {
1078 		strlcat(buf, ": ", sizeof(buf));
1079 		strlcat(buf, strerror(errnum), sizeof(buf));
1080 	}
1081 
1082 	if (background)
1083 		syslog(log_pri, "%s", buf);
1084 	else
1085 		warnx("%s", buf);
1086 }
1087 
1088 /**
1089  ** Mouse interface code, courtesy of XFree86 3.1.2.
1090  **
1091  ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
1092  ** to clean, reformat and rationalise naming, it's quite possible that
1093  ** some things in here have been broken.
1094  **
1095  ** I hope not 8)
1096  **
1097  ** The following code is derived from a module marked :
1098  **/
1099 
1100 /* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
1101 /* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
1102  17:03:40 dawes Exp $ */
1103 /*
1104  *
1105  * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
1106  * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
1107  *
1108  * Permission to use, copy, modify, distribute, and sell this software and its
1109  * documentation for any purpose is hereby granted without fee, provided that
1110  * the above copyright notice appear in all copies and that both that
1111  * copyright notice and this permission notice appear in supporting
1112  * documentation, and that the names of Thomas Roell and David Dawes not be
1113  * used in advertising or publicity pertaining to distribution of the
1114  * software without specific, written prior permission.  Thomas Roell
1115  * and David Dawes makes no representations about the suitability of this
1116  * software for any purpose.  It is provided "as is" without express or
1117  * implied warranty.
1118  *
1119  * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
1120  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
1121  * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
1122  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
1123  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
1124  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1125  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1126  *
1127  */
1128 
1129 /**
1130  ** GlidePoint support from XFree86 3.2.
1131  ** Derived from the module:
1132  **/
1133 
1134 /* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.19 1996/10/16 14:40:51 dawes Exp $ */
1135 /* $XConsortium: xf86_Mouse.c /main/10 1996/01/30 15:16:12 kaleb $ */
1136 
1137 /* the following table must be ordered by MOUSE_PROTO_XXX in mouse.h */
1138 static unsigned char proto[][7] = {
1139     /*  hd_mask hd_id   dp_mask dp_id   bytes b4_mask b4_id */
1140     {	0x40,	0x40,	0x40,	0x00,	3,   ~0x23,  0x00 }, /* MicroSoft */
1141     {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* MouseSystems */
1142     {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* Logitech */
1143     {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* MMSeries */
1144     {	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* MouseMan */
1145     {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* Bus */
1146     {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* InPort */
1147     {	0xc0,	0x00,	0x00,	0x00,	3,    0x00,  0xff }, /* PS/2 mouse */
1148     {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* MM HitTablet */
1149     {	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* GlidePoint */
1150     {	0x40,	0x40,	0x40,	0x00,	3,   ~0x3f,  0x00 }, /* IntelliMouse */
1151     {	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* ThinkingMouse */
1152     {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* sysmouse */
1153     {	0x40,	0x40,	0x40,	0x00,	3,   ~0x23,  0x00 }, /* X10 MouseRem */
1154     {	0x80,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* KIDSPAD */
1155     {	0xc3,	0xc0,	0x00,	0x00,	6,    0x00,  0xff }, /* VersaPad */
1156     {	0x00,	0x00,	0x00,	0x00,	1,    0x00,  0xff }, /* JogDial */
1157 #if notyet
1158     {	0xf8,	0x80,	0x00,	0x00,	5,   ~0x2f,  0x10 }, /* Mariqua */
1159 #endif
1160 };
1161 static unsigned char cur_proto[7];
1162 
1163 static int
1164 r_identify(void)
1165 {
1166     char pnpbuf[256];	/* PnP identifier string may be up to 256 bytes long */
1167     pnpid_t pnpid;
1168     symtab_t *t;
1169     int level;
1170     int len;
1171 
1172     /* set the driver operation level, if applicable */
1173     if (rodent.level < 0)
1174 	rodent.level = 1;
1175     ioctl(rodent.mfd, MOUSE_SETLEVEL, &rodent.level);
1176     rodent.level = (ioctl(rodent.mfd, MOUSE_GETLEVEL, &level) == 0) ? level : 0;
1177 
1178     /*
1179      * Interrogate the driver and get some intelligence on the device...
1180      * The following ioctl functions are not always supported by device
1181      * drivers.  When the driver doesn't support them, we just trust the
1182      * user to supply valid information.
1183      */
1184     rodent.hw.iftype = MOUSE_IF_UNKNOWN;
1185     rodent.hw.model = MOUSE_MODEL_GENERIC;
1186     ioctl(rodent.mfd, MOUSE_GETHWINFO, &rodent.hw);
1187 
1188     if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1189 	bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
1190     rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
1191     rodent.mode.rate = -1;
1192     rodent.mode.resolution = MOUSE_RES_UNKNOWN;
1193     rodent.mode.accelfactor = 0;
1194     rodent.mode.level = 0;
1195     if (ioctl(rodent.mfd, MOUSE_GETMODE, &rodent.mode) == 0) {
1196 	if ((rodent.mode.protocol == MOUSE_PROTO_UNKNOWN)
1197 	    || (rodent.mode.protocol >= sizeof(proto)/sizeof(proto[0]))) {
1198 	    logwarnx("unknown mouse protocol (%d)", rodent.mode.protocol);
1199 	    return MOUSE_PROTO_UNKNOWN;
1200 	} else {
1201 	    /* INPORT and BUS are the same... */
1202 	    if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
1203 		rodent.mode.protocol = MOUSE_PROTO_BUS;
1204 	    if (rodent.mode.protocol != rodent.rtype) {
1205 		/* Hmm, the driver doesn't agree with the user... */
1206 		if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1207 		    logwarnx("mouse type mismatch (%s != %s), %s is assumed",
1208 			r_name(rodent.mode.protocol), r_name(rodent.rtype),
1209 			r_name(rodent.mode.protocol));
1210 		rodent.rtype = rodent.mode.protocol;
1211 		bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
1212 	    }
1213 	}
1214 	cur_proto[4] = rodent.mode.packetsize;
1215 	cur_proto[0] = rodent.mode.syncmask[0];	/* header byte bit mask */
1216 	cur_proto[1] = rodent.mode.syncmask[1];	/* header bit pattern */
1217     }
1218 
1219     /* maybe this is a PnP mouse... */
1220     if (rodent.mode.protocol == MOUSE_PROTO_UNKNOWN) {
1221 
1222 	if (rodent.flags & NoPnP)
1223 	    return rodent.rtype;
1224 	if (((len = pnpgets(pnpbuf)) <= 0) || !pnpparse(&pnpid, pnpbuf, len))
1225 	    return rodent.rtype;
1226 
1227 	debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
1228 	    pnpid.neisaid, pnpid.neisaid, pnpid.eisaid,
1229 	    pnpid.ncompat, pnpid.ncompat, pnpid.compat,
1230 	    pnpid.ndescription, pnpid.ndescription, pnpid.description);
1231 
1232 	/* we have a valid PnP serial device ID */
1233 	rodent.hw.iftype = MOUSE_IF_SERIAL;
1234 	t = pnpproto(&pnpid);
1235 	if (t != NULL) {
1236 	    rodent.mode.protocol = t->val;
1237 	    rodent.hw.model = t->val2;
1238 	} else {
1239 	    rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
1240 	}
1241 	if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
1242 	    rodent.mode.protocol = MOUSE_PROTO_BUS;
1243 
1244 	/* make final adjustment */
1245 	if (rodent.mode.protocol != MOUSE_PROTO_UNKNOWN) {
1246 	    if (rodent.mode.protocol != rodent.rtype) {
1247 		/* Hmm, the device doesn't agree with the user... */
1248 		if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1249 		    logwarnx("mouse type mismatch (%s != %s), %s is assumed",
1250 			r_name(rodent.mode.protocol), r_name(rodent.rtype),
1251 			r_name(rodent.mode.protocol));
1252 		rodent.rtype = rodent.mode.protocol;
1253 		bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
1254 	    }
1255 	}
1256     }
1257 
1258     debug("proto params: %02x %02x %02x %02x %d %02x %02x",
1259 	cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
1260 	cur_proto[4], cur_proto[5], cur_proto[6]);
1261 
1262     return rodent.rtype;
1263 }
1264 
1265 static char *
1266 r_if(int iftype)
1267 {
1268     char *s;
1269 
1270     s = gettokenname(rifs, iftype);
1271     return (s == NULL) ? "unknown" : s;
1272 }
1273 
1274 static char *
1275 r_name(int type)
1276 {
1277     return ((type == MOUSE_PROTO_UNKNOWN)
1278 	|| (type > sizeof(rnames)/sizeof(rnames[0]) - 1))
1279 	? "unknown" : rnames[type];
1280 }
1281 
1282 static char *
1283 r_model(int model)
1284 {
1285     char *s;
1286 
1287     s = gettokenname(rmodels, model);
1288     return (s == NULL) ? "unknown" : s;
1289 }
1290 
1291 static void
1292 r_init(void)
1293 {
1294     unsigned char buf[16];	/* scrach buffer */
1295     fd_set fds;
1296     char *s;
1297     char c;
1298     int i;
1299 
1300     /**
1301      ** This comment is a little out of context here, but it contains
1302      ** some useful information...
1303      ********************************************************************
1304      **
1305      ** The following lines take care of the Logitech MouseMan protocols.
1306      **
1307      ** NOTE: There are different versions of both MouseMan and TrackMan!
1308      **       Hence I add another protocol P_LOGIMAN, which the user can
1309      **       specify as MouseMan in his XF86Config file. This entry was
1310      **       formerly handled as a special case of P_MS. However, people
1311      **       who don't have the middle button problem, can still specify
1312      **       Microsoft and use P_MS.
1313      **
1314      ** By default, these mice should use a 3 byte Microsoft protocol
1315      ** plus a 4th byte for the middle button. However, the mouse might
1316      ** have switched to a different protocol before we use it, so I send
1317      ** the proper sequence just in case.
1318      **
1319      ** NOTE: - all commands to (at least the European) MouseMan have to
1320      **         be sent at 1200 Baud.
1321      **       - each command starts with a '*'.
1322      **       - whenever the MouseMan receives a '*', it will switch back
1323      **	 to 1200 Baud. Hence I have to select the desired protocol
1324      **	 first, then select the baud rate.
1325      **
1326      ** The protocols supported by the (European) MouseMan are:
1327      **   -  5 byte packed binary protocol, as with the Mouse Systems
1328      **      mouse. Selected by sequence "*U".
1329      **   -  2 button 3 byte MicroSoft compatible protocol. Selected
1330      **      by sequence "*V".
1331      **   -  3 button 3+1 byte MicroSoft compatible protocol (default).
1332      **      Selected by sequence "*X".
1333      **
1334      ** The following baud rates are supported:
1335      **   -  1200 Baud (default). Selected by sequence "*n".
1336      **   -  9600 Baud. Selected by sequence "*q".
1337      **
1338      ** Selecting a sample rate is no longer supported with the MouseMan!
1339      ** Some additional lines in xf86Config.c take care of ill configured
1340      ** baud rates and sample rates. (The user will get an error.)
1341      */
1342 
1343     switch (rodent.rtype) {
1344 
1345     case MOUSE_PROTO_LOGI:
1346 	/*
1347 	 * The baud rate selection command must be sent at the current
1348 	 * baud rate; try all likely settings
1349 	 */
1350 	setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
1351 	setmousespeed(4800, rodent.baudrate, rodentcflags[rodent.rtype]);
1352 	setmousespeed(2400, rodent.baudrate, rodentcflags[rodent.rtype]);
1353 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1354 	/* select MM series data format */
1355 	write(rodent.mfd, "S", 1);
1356 	setmousespeed(rodent.baudrate, rodent.baudrate,
1357 		      rodentcflags[MOUSE_PROTO_MM]);
1358 	/* select report rate/frequency */
1359 	if      (rodent.rate <= 0)   write(rodent.mfd, "O", 1);
1360 	else if (rodent.rate <= 15)  write(rodent.mfd, "J", 1);
1361 	else if (rodent.rate <= 27)  write(rodent.mfd, "K", 1);
1362 	else if (rodent.rate <= 42)  write(rodent.mfd, "L", 1);
1363 	else if (rodent.rate <= 60)  write(rodent.mfd, "R", 1);
1364 	else if (rodent.rate <= 85)  write(rodent.mfd, "M", 1);
1365 	else if (rodent.rate <= 125) write(rodent.mfd, "Q", 1);
1366 	else			     write(rodent.mfd, "N", 1);
1367 	break;
1368 
1369     case MOUSE_PROTO_LOGIMOUSEMAN:
1370 	/* The command must always be sent at 1200 baud */
1371 	setmousespeed(1200, 1200, rodentcflags[rodent.rtype]);
1372 	write(rodent.mfd, "*X", 2);
1373 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1374 	break;
1375 
1376     case MOUSE_PROTO_HITTAB:
1377 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1378 
1379 	/*
1380 	 * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
1381 	 * The tablet must be configured to be in MM mode, NO parity,
1382 	 * Binary Format.  xf86Info.sampleRate controls the sensativity
1383 	 * of the tablet.  We only use this tablet for it's 4-button puck
1384 	 * so we don't run in "Absolute Mode"
1385 	 */
1386 	write(rodent.mfd, "z8", 2);	/* Set Parity = "NONE" */
1387 	usleep(50000);
1388 	write(rodent.mfd, "zb", 2);	/* Set Format = "Binary" */
1389 	usleep(50000);
1390 	write(rodent.mfd, "@", 1);	/* Set Report Mode = "Stream" */
1391 	usleep(50000);
1392 	write(rodent.mfd, "R", 1);	/* Set Output Rate = "45 rps" */
1393 	usleep(50000);
1394 	write(rodent.mfd, "I\x20", 2);	/* Set Incrememtal Mode "20" */
1395 	usleep(50000);
1396 	write(rodent.mfd, "E", 1);	/* Set Data Type = "Relative */
1397 	usleep(50000);
1398 
1399 	/* Resolution is in 'lines per inch' on the Hitachi tablet */
1400 	if      (rodent.resolution == MOUSE_RES_LOW)		c = 'g';
1401 	else if (rodent.resolution == MOUSE_RES_MEDIUMLOW)	c = 'e';
1402 	else if (rodent.resolution == MOUSE_RES_MEDIUMHIGH)	c = 'h';
1403 	else if (rodent.resolution == MOUSE_RES_HIGH)		c = 'd';
1404 	else if (rodent.resolution <=   40)			c = 'g';
1405 	else if (rodent.resolution <=  100)			c = 'd';
1406 	else if (rodent.resolution <=  200)			c = 'e';
1407 	else if (rodent.resolution <=  500)			c = 'h';
1408 	else if (rodent.resolution <= 1000)			c = 'j';
1409 	else			c = 'd';
1410 	write(rodent.mfd, &c, 1);
1411 	usleep(50000);
1412 
1413 	write(rodent.mfd, "\021", 1);	/* Resume DATA output */
1414 	break;
1415 
1416     case MOUSE_PROTO_THINK:
1417 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1418 	/* the PnP ID string may be sent again, discard it */
1419 	usleep(200000);
1420 	i = FREAD;
1421 	ioctl(rodent.mfd, TIOCFLUSH, &i);
1422 	/* send the command to initialize the beast */
1423 	for (s = "E5E5"; *s; ++s) {
1424 	    write(rodent.mfd, s, 1);
1425 	    FD_ZERO(&fds);
1426 	    FD_SET(rodent.mfd, &fds);
1427 	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1428 		break;
1429 	    read(rodent.mfd, &c, 1);
1430 	    debug("%c", c);
1431 	    if (c != *s)
1432 		break;
1433 	}
1434 	break;
1435 
1436     case MOUSE_PROTO_JOGDIAL:
1437 	break;
1438     case MOUSE_PROTO_MSC:
1439 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1440 	if (rodent.flags & ClearDTR) {
1441 	   i = TIOCM_DTR;
1442 	   ioctl(rodent.mfd, TIOCMBIC, &i);
1443 	}
1444 	if (rodent.flags & ClearRTS) {
1445 	   i = TIOCM_RTS;
1446 	   ioctl(rodent.mfd, TIOCMBIC, &i);
1447 	}
1448 	break;
1449 
1450     case MOUSE_PROTO_SYSMOUSE:
1451 	if (rodent.hw.iftype == MOUSE_IF_SYSMOUSE)
1452 	    setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1453 	/* FALLTHROUGH */
1454 
1455     case MOUSE_PROTO_BUS:
1456     case MOUSE_PROTO_INPORT:
1457     case MOUSE_PROTO_PS2:
1458 	if (rodent.rate >= 0)
1459 	    rodent.mode.rate = rodent.rate;
1460 	if (rodent.resolution != MOUSE_RES_UNKNOWN)
1461 	    rodent.mode.resolution = rodent.resolution;
1462 	ioctl(rodent.mfd, MOUSE_SETMODE, &rodent.mode);
1463 	break;
1464 
1465     case MOUSE_PROTO_X10MOUSEREM:
1466 	mremote_serversetup();
1467 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1468 	break;
1469 
1470 
1471     case MOUSE_PROTO_VERSAPAD:
1472 	tcsendbreak(rodent.mfd, 0);	/* send break for 400 msec */
1473 	i = FREAD;
1474 	ioctl(rodent.mfd, TIOCFLUSH, &i);
1475 	for (i = 0; i < 7; ++i) {
1476 	    FD_ZERO(&fds);
1477 	    FD_SET(rodent.mfd, &fds);
1478 	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1479 		break;
1480 	    read(rodent.mfd, &c, 1);
1481 	    buf[i] = c;
1482 	}
1483 	debug("%s\n", buf);
1484 	if ((buf[0] != 'V') || (buf[1] != 'P')|| (buf[7] != '\r'))
1485 	    break;
1486 	setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
1487 	tcsendbreak(rodent.mfd, 0);	/* send break for 400 msec again */
1488 	for (i = 0; i < 7; ++i) {
1489 	    FD_ZERO(&fds);
1490 	    FD_SET(rodent.mfd, &fds);
1491 	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1492 		break;
1493 	    read(rodent.mfd, &c, 1);
1494 	    debug("%c", c);
1495 	    if (c != buf[i])
1496 		break;
1497 	}
1498 	i = FREAD;
1499 	ioctl(rodent.mfd, TIOCFLUSH, &i);
1500 	break;
1501 
1502     default:
1503 	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1504 	break;
1505     }
1506 }
1507 
1508 static int
1509 r_protocol(u_char rBuf, mousestatus_t *act)
1510 {
1511     /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1512     static int butmapmss[4] = {	/* Microsoft, MouseMan, GlidePoint,
1513 				   IntelliMouse, Thinking Mouse */
1514 	0,
1515 	MOUSE_BUTTON3DOWN,
1516 	MOUSE_BUTTON1DOWN,
1517 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1518     };
1519     static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
1520 				    Thinking Mouse */
1521 	0,
1522 	MOUSE_BUTTON4DOWN,
1523 	MOUSE_BUTTON2DOWN,
1524 	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1525     };
1526     /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1527     static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
1528 				       MouseMan+ */
1529 	0,
1530 	MOUSE_BUTTON2DOWN,
1531 	MOUSE_BUTTON4DOWN,
1532 	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1533     };
1534     /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
1535     static int butmapmsc[8] = {	/* MouseSystems, MMSeries, Logitech,
1536 				   Bus, sysmouse */
1537 	0,
1538 	MOUSE_BUTTON3DOWN,
1539 	MOUSE_BUTTON2DOWN,
1540 	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1541 	MOUSE_BUTTON1DOWN,
1542 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1543 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1544 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1545     };
1546     /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1547     static int butmapps2[8] = {	/* PS/2 */
1548 	0,
1549 	MOUSE_BUTTON1DOWN,
1550 	MOUSE_BUTTON3DOWN,
1551 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1552 	MOUSE_BUTTON2DOWN,
1553 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1554 	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1555 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1556     };
1557     /* for Hitachi tablet */
1558     static int butmaphit[8] = {	/* MM HitTablet */
1559 	0,
1560 	MOUSE_BUTTON3DOWN,
1561 	MOUSE_BUTTON2DOWN,
1562 	MOUSE_BUTTON1DOWN,
1563 	MOUSE_BUTTON4DOWN,
1564 	MOUSE_BUTTON5DOWN,
1565 	MOUSE_BUTTON6DOWN,
1566 	MOUSE_BUTTON7DOWN,
1567     };
1568     /* for serial VersaPad */
1569     static int butmapversa[8] = { /* VersaPad */
1570 	0,
1571 	0,
1572 	MOUSE_BUTTON3DOWN,
1573 	MOUSE_BUTTON3DOWN,
1574 	MOUSE_BUTTON1DOWN,
1575 	MOUSE_BUTTON1DOWN,
1576 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1577 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1578     };
1579     /* for PS/2 VersaPad */
1580     static int butmapversaps2[8] = { /* VersaPad */
1581 	0,
1582 	MOUSE_BUTTON3DOWN,
1583 	0,
1584 	MOUSE_BUTTON3DOWN,
1585 	MOUSE_BUTTON1DOWN,
1586 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1587 	MOUSE_BUTTON1DOWN,
1588 	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1589     };
1590     static int           pBufP = 0;
1591     static unsigned char pBuf[8];
1592     static int		 prev_x, prev_y;
1593     static int		 on = FALSE;
1594     int			 x, y;
1595 
1596     debug("received char 0x%x",(int)rBuf);
1597     if (rodent.rtype == MOUSE_PROTO_KIDSPAD)
1598 	return kidspad(rBuf, act) ;
1599 
1600     /*
1601      * Hack for resyncing: We check here for a package that is:
1602      *  a) illegal (detected by wrong data-package header)
1603      *  b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
1604      *  c) bad header-package
1605      *
1606      * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
1607      *       -128 are allowed, but since they are very seldom we can easily
1608      *       use them as package-header with no button pressed.
1609      * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
1610      *         0x80 is not valid as a header byte. For a PS/2 mouse we skip
1611      *         checking data bytes.
1612      *         For resyncing a PS/2 mouse we require the two most significant
1613      *         bits in the header byte to be 0. These are the overflow bits,
1614      *         and in case of an overflow we actually lose sync. Overflows
1615      *         are very rare, however, and we quickly gain sync again after
1616      *         an overflow condition. This is the best we can do. (Actually,
1617      *         we could use bit 0x08 in the header byte for resyncing, since
1618      *         that bit is supposed to be always on, but nobody told
1619      *         Microsoft...)
1620      */
1621 
1622     if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
1623 	((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
1624     {
1625 	pBufP = 0;		/* skip package */
1626     }
1627 
1628     if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
1629 	return 0;
1630 
1631     /* is there an extra data byte? */
1632     if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
1633     {
1634 	/*
1635 	 * Hack for Logitech MouseMan Mouse - Middle button
1636 	 *
1637 	 * Unfortunately this mouse has variable length packets: the standard
1638 	 * Microsoft 3 byte packet plus an optional 4th byte whenever the
1639 	 * middle button status changes.
1640 	 *
1641 	 * We have already processed the standard packet with the movement
1642 	 * and button info.  Now post an event message with the old status
1643 	 * of the left and right buttons and the updated middle button.
1644 	 */
1645 
1646 	/*
1647 	 * Even worse, different MouseMen and TrackMen differ in the 4th
1648 	 * byte: some will send 0x00/0x20, others 0x01/0x21, or even
1649 	 * 0x02/0x22, so I have to strip off the lower bits.
1650 	 *
1651 	 * [JCH-96/01/21]
1652 	 * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
1653 	 * and it is activated by tapping the glidepad with the finger! 8^)
1654 	 * We map it to bit bit3, and the reverse map in xf86Events just has
1655 	 * to be extended so that it is identified as Button 4. The lower
1656 	 * half of the reverse-map may remain unchanged.
1657 	 */
1658 
1659 	/*
1660 	 * [KY-97/08/03]
1661 	 * Receive the fourth byte only when preceding three bytes have
1662 	 * been detected (pBufP >= cur_proto[4]).  In the previous
1663 	 * versions, the test was pBufP == 0; thus, we may have mistakingly
1664 	 * received a byte even if we didn't see anything preceding
1665 	 * the byte.
1666 	 */
1667 
1668 	if ((rBuf & cur_proto[5]) != cur_proto[6]) {
1669 	    pBufP = 0;
1670 	    return 0;
1671 	}
1672 
1673 	switch (rodent.rtype) {
1674 #if notyet
1675 	case MOUSE_PROTO_MARIQUA:
1676 	    /*
1677 	     * This mouse has 16! buttons in addition to the standard
1678 	     * three of them.  They return 0x10 though 0x1f in the
1679 	     * so-called `ten key' mode and 0x30 though 0x3f in the
1680 	     * `function key' mode.  As there are only 31 bits for
1681 	     * button state (including the standard three), we ignore
1682 	     * the bit 0x20 and don't distinguish the two modes.
1683 	     */
1684 	    act->dx = act->dy = act->dz = 0;
1685 	    act->obutton = act->button;
1686 	    rBuf &= 0x1f;
1687 	    act->button = (1 << (rBuf - 13))
1688 		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1689 	    /*
1690 	     * FIXME: this is a button "down" event. There needs to be
1691 	     * a corresponding button "up" event... XXX
1692 	     */
1693 	    break;
1694 #endif /* notyet */
1695 	case MOUSE_PROTO_JOGDIAL:
1696 	    break;
1697 
1698 	/*
1699 	 * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
1700 	 * always send the fourth byte, whereas the fourth byte is
1701 	 * optional for GlidePoint and ThinkingMouse. The fourth byte
1702 	 * is also optional for MouseMan+ and FirstMouse+ in their
1703 	 * native mode. It is always sent if they are in the IntelliMouse
1704 	 * compatible mode.
1705 	 */
1706 	case MOUSE_PROTO_INTELLI:	/* IntelliMouse, NetMouse, Mie Mouse,
1707 					   MouseMan+ */
1708 	    act->dx = act->dy = 0;
1709 	    act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
1710 	    if ((act->dz >= 7) || (act->dz <= -7))
1711 		act->dz = 0;
1712 	    act->obutton = act->button;
1713 	    act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1714 		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1715 	    break;
1716 
1717 	default:
1718 	    act->dx = act->dy = act->dz = 0;
1719 	    act->obutton = act->button;
1720 	    act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1721 		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1722 	    break;
1723 	}
1724 
1725 	act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1726 	    | (act->obutton ^ act->button);
1727 	pBufP = 0;
1728 	return act->flags;
1729     }
1730 
1731     if (pBufP >= cur_proto[4])
1732 	pBufP = 0;
1733     pBuf[pBufP++] = rBuf;
1734     if (pBufP != cur_proto[4])
1735 	return 0;
1736 
1737     /*
1738      * assembly full package
1739      */
1740 
1741     debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
1742 	cur_proto[4],
1743 	pBuf[0], pBuf[1], pBuf[2], pBuf[3],
1744 	pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
1745 
1746     act->dz = 0;
1747     act->obutton = act->button;
1748     switch (rodent.rtype)
1749     {
1750     case MOUSE_PROTO_MS:		/* Microsoft */
1751     case MOUSE_PROTO_LOGIMOUSEMAN:	/* MouseMan/TrackMan */
1752     case MOUSE_PROTO_X10MOUSEREM:	/* X10 MouseRemote */
1753 	act->button = act->obutton & MOUSE_BUTTON4DOWN;
1754 	if (rodent.flags & ChordMiddle)
1755 	    act->button |= ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
1756 		? MOUSE_BUTTON2DOWN
1757 		: butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1758 	else
1759 	    act->button |= (act->obutton & MOUSE_BUTTON2DOWN)
1760 		| butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1761 
1762 	/* Send X10 btn events to remote client (ensure -128-+127 range) */
1763 	if ((rodent.rtype == MOUSE_PROTO_X10MOUSEREM) &&
1764 	    ((pBuf[0] & 0xFC) == 0x44) && (pBuf[2] == 0x3F)) {
1765 	    if (rodent.mremcfd >= 0) {
1766 		unsigned char key = (signed char)(((pBuf[0] & 0x03) << 6) |
1767 						  (pBuf[1] & 0x3F));
1768 		write(rodent.mremcfd, &key, 1);
1769 	    }
1770 	    return 0;
1771 	}
1772 
1773 	act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1774 	act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1775 	break;
1776 
1777     case MOUSE_PROTO_GLIDEPOINT:	/* GlidePoint */
1778     case MOUSE_PROTO_THINK:		/* ThinkingMouse */
1779     case MOUSE_PROTO_INTELLI:		/* IntelliMouse, NetMouse, Mie Mouse,
1780 					   MouseMan+ */
1781 	act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
1782 	    | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1783 	act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1784 	act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1785 	break;
1786 
1787     case MOUSE_PROTO_MSC:		/* MouseSystems Corp */
1788 #if notyet
1789     case MOUSE_PROTO_MARIQUA:		/* Mariqua */
1790 #endif
1791 	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1792 	act->dx =    (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
1793 	act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
1794 	break;
1795 
1796     case MOUSE_PROTO_JOGDIAL:		/* JogDial */
1797 	    if (rBuf == 0x6c)
1798 	      act->dz = -1;
1799 	    if (rBuf == 0x72)
1800 	      act->dz = 1;
1801 	    if (rBuf == 0x64)
1802 	      act->button = MOUSE_BUTTON1DOWN;
1803 	    if (rBuf == 0x75)
1804 	      act->button = 0;
1805 	break;
1806 
1807     case MOUSE_PROTO_HITTAB:		/* MM HitTablet */
1808 	act->button = butmaphit[pBuf[0] & 0x07];
1809 	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1810 	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1811 	break;
1812 
1813     case MOUSE_PROTO_MM:		/* MM Series */
1814     case MOUSE_PROTO_LOGI:		/* Logitech Mice */
1815 	act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
1816 	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1817 	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1818 	break;
1819 
1820     case MOUSE_PROTO_VERSAPAD:		/* VersaPad */
1821 	act->button = butmapversa[(pBuf[0] & MOUSE_VERSA_BUTTONS) >> 3];
1822 	act->button |= (pBuf[0] & MOUSE_VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
1823 	act->dx = act->dy = 0;
1824 	if (!(pBuf[0] & MOUSE_VERSA_IN_USE)) {
1825 	    on = FALSE;
1826 	    break;
1827 	}
1828 	x = (pBuf[2] << 6) | pBuf[1];
1829 	if (x & 0x800)
1830 	    x -= 0x1000;
1831 	y = (pBuf[4] << 6) | pBuf[3];
1832 	if (y & 0x800)
1833 	    y -= 0x1000;
1834 	if (on) {
1835 	    act->dx = prev_x - x;
1836 	    act->dy = prev_y - y;
1837 	} else {
1838 	    on = TRUE;
1839 	}
1840 	prev_x = x;
1841 	prev_y = y;
1842 	break;
1843 
1844     case MOUSE_PROTO_BUS:		/* Bus */
1845     case MOUSE_PROTO_INPORT:		/* InPort */
1846 	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1847 	act->dx =   (signed char)pBuf[1];
1848 	act->dy = - (signed char)pBuf[2];
1849 	break;
1850 
1851     case MOUSE_PROTO_PS2:		/* PS/2 */
1852 	act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
1853 	act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ?    pBuf[1] - 256  :  pBuf[1];
1854 	act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ?  -(pBuf[2] - 256) : -pBuf[2];
1855 	/*
1856 	 * Moused usually operates the psm driver at the operation level 1
1857 	 * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
1858 	 * The following code takes effect only when the user explicitly
1859 	 * requets the level 2 at which wheel movement and additional button
1860 	 * actions are encoded in model-dependent formats. At the level 0
1861 	 * the following code is no-op because the psm driver says the model
1862 	 * is MOUSE_MODEL_GENERIC.
1863 	 */
1864 	switch (rodent.hw.model) {
1865 	case MOUSE_MODEL_EXPLORER:
1866 	    /* wheel and additional button data is in the fourth byte */
1867 	    act->dz = (pBuf[3] & MOUSE_EXPLORER_ZNEG)
1868 		? (pBuf[3] & 0x0f) - 16 : (pBuf[3] & 0x0f);
1869 	    act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON4DOWN)
1870 		? MOUSE_BUTTON4DOWN : 0;
1871 	    act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON5DOWN)
1872 		? MOUSE_BUTTON5DOWN : 0;
1873 	    break;
1874 	case MOUSE_MODEL_INTELLI:
1875 	case MOUSE_MODEL_NET:
1876 	    /* wheel data is in the fourth byte */
1877 	    act->dz = (signed char)pBuf[3];
1878 	    if ((act->dz >= 7) || (act->dz <= -7))
1879 		act->dz = 0;
1880 	    /* some compatible mice may have additional buttons */
1881 	    act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON4DOWN)
1882 		? MOUSE_BUTTON4DOWN : 0;
1883 	    act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON5DOWN)
1884 		? MOUSE_BUTTON5DOWN : 0;
1885 	    break;
1886 	case MOUSE_MODEL_MOUSEMANPLUS:
1887 	    if (((pBuf[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
1888 		    && (abs(act->dx) > 191)
1889 		    && MOUSE_PS2PLUS_CHECKBITS(pBuf)) {
1890 		/* the extended data packet encodes button and wheel events */
1891 		switch (MOUSE_PS2PLUS_PACKET_TYPE(pBuf)) {
1892 		case 1:
1893 		    /* wheel data packet */
1894 		    act->dx = act->dy = 0;
1895 		    if (pBuf[2] & 0x80) {
1896 			/* horizontal roller count - ignore it XXX*/
1897 		    } else {
1898 			/* vertical roller count */
1899 			act->dz = (pBuf[2] & MOUSE_PS2PLUS_ZNEG)
1900 			    ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
1901 		    }
1902 		    act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
1903 			? MOUSE_BUTTON4DOWN : 0;
1904 		    act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
1905 			? MOUSE_BUTTON5DOWN : 0;
1906 		    break;
1907 		case 2:
1908 		    /* this packet type is reserved by Logitech */
1909 		    /*
1910 		     * IBM ScrollPoint Mouse uses this packet type to
1911 		     * encode both vertical and horizontal scroll movement.
1912 		     */
1913 		    act->dx = act->dy = 0;
1914 		    /* horizontal roller count */
1915 		    if (pBuf[2] & 0x0f)
1916 			act->dz = (pBuf[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
1917 		    /* vertical roller count */
1918 		    if (pBuf[2] & 0xf0)
1919 			act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
1920 #if 0
1921 		    /* vertical roller count */
1922 		    act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG)
1923 			? ((pBuf[2] >> 4) & 0x0f) - 16
1924 			: ((pBuf[2] >> 4) & 0x0f);
1925 		    /* horizontal roller count */
1926 		    act->dw = (pBuf[2] & MOUSE_SPOINT_WNEG)
1927 			? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
1928 #endif
1929 		    break;
1930 		case 0:
1931 		    /* device type packet - shouldn't happen */
1932 		    /* FALLTHROUGH */
1933 		default:
1934 		    act->dx = act->dy = 0;
1935 		    act->button = act->obutton;
1936 		    debug("unknown PS2++ packet type %d: 0x%02x 0x%02x 0x%02x\n",
1937 			  MOUSE_PS2PLUS_PACKET_TYPE(pBuf),
1938 			  pBuf[0], pBuf[1], pBuf[2]);
1939 		    break;
1940 		}
1941 	    } else {
1942 		/* preserve button states */
1943 		act->button |= act->obutton & MOUSE_EXTBUTTONS;
1944 	    }
1945 	    break;
1946 	case MOUSE_MODEL_GLIDEPOINT:
1947 	    /* `tapping' action */
1948 	    act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
1949 	    break;
1950 	case MOUSE_MODEL_NETSCROLL:
1951 	    /* three addtional bytes encode buttons and wheel events */
1952 	    act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
1953 		? MOUSE_BUTTON4DOWN : 0;
1954 	    act->button |= (pBuf[3] & MOUSE_PS2_BUTTON1DOWN)
1955 		? MOUSE_BUTTON5DOWN : 0;
1956 	    act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
1957 	    break;
1958 	case MOUSE_MODEL_THINK:
1959 	    /* the fourth button state in the first byte */
1960 	    act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
1961 	    break;
1962 	case MOUSE_MODEL_VERSAPAD:
1963 	    act->button = butmapversaps2[pBuf[0] & MOUSE_PS2VERSA_BUTTONS];
1964 	    act->button |=
1965 		(pBuf[0] & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
1966 	    act->dx = act->dy = 0;
1967 	    if (!(pBuf[0] & MOUSE_PS2VERSA_IN_USE)) {
1968 		on = FALSE;
1969 		break;
1970 	    }
1971 	    x = ((pBuf[4] << 8) & 0xf00) | pBuf[1];
1972 	    if (x & 0x800)
1973 		x -= 0x1000;
1974 	    y = ((pBuf[4] << 4) & 0xf00) | pBuf[2];
1975 	    if (y & 0x800)
1976 		y -= 0x1000;
1977 	    if (on) {
1978 		act->dx = prev_x - x;
1979 		act->dy = prev_y - y;
1980 	    } else {
1981 		on = TRUE;
1982 	    }
1983 	    prev_x = x;
1984 	    prev_y = y;
1985 	    break;
1986 	case MOUSE_MODEL_4D:
1987 	    act->dx = (pBuf[1] & 0x80) ?    pBuf[1] - 256  :  pBuf[1];
1988 	    act->dy = (pBuf[2] & 0x80) ?  -(pBuf[2] - 256) : -pBuf[2];
1989 	    switch (pBuf[0] & MOUSE_4D_WHEELBITS) {
1990 	    case 0x10:
1991 		act->dz = 1;
1992 		break;
1993 	    case 0x30:
1994 		act->dz = -1;
1995 		break;
1996 	    case 0x40:	/* 2nd wheel rolling right XXX */
1997 		act->dz = 2;
1998 		break;
1999 	    case 0xc0:	/* 2nd wheel rolling left XXX */
2000 		act->dz = -2;
2001 		break;
2002 	    }
2003 	    break;
2004 	case MOUSE_MODEL_4DPLUS:
2005 	    if ((act->dx < 16 - 256) && (act->dy > 256 - 16)) {
2006 		act->dx = act->dy = 0;
2007 		if (pBuf[2] & MOUSE_4DPLUS_BUTTON4DOWN)
2008 		    act->button |= MOUSE_BUTTON4DOWN;
2009 		act->dz = (pBuf[2] & MOUSE_4DPLUS_ZNEG)
2010 			      ? ((pBuf[2] & 0x07) - 8) : (pBuf[2] & 0x07);
2011 	    } else {
2012 		/* preserve previous button states */
2013 		act->button |= act->obutton & MOUSE_EXTBUTTONS;
2014 	    }
2015 	    break;
2016 	case MOUSE_MODEL_GENERIC:
2017 	default:
2018 	    break;
2019 	}
2020 	break;
2021 
2022     case MOUSE_PROTO_SYSMOUSE:		/* sysmouse */
2023 	act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
2024 	act->dx =    (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
2025 	act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
2026 	if (rodent.level == 1) {
2027 	    act->dz = ((signed char)(pBuf[5] << 1) + (signed char)(pBuf[6] << 1)) >> 1;
2028 	    act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
2029 	}
2030 	break;
2031 
2032     default:
2033 	return 0;
2034     }
2035     /*
2036      * We don't reset pBufP here yet, as there may be an additional data
2037      * byte in some protocols. See above.
2038      */
2039 
2040     /* has something changed? */
2041     act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
2042 	| (act->obutton ^ act->button);
2043 
2044     return act->flags;
2045 }
2046 
2047 static int
2048 r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans)
2049 {
2050     int changed;
2051     int flags;
2052 
2053     a2->dx = a1->dx;
2054     a2->dy = a1->dy;
2055     a2->dz = a1->dz;
2056     a2->obutton = a2->button;
2057     a2->button = a1->button;
2058     a2->flags = a1->flags;
2059     changed = FALSE;
2060 
2061     if (rodent.flags & Emulate3Button) {
2062 	if (debug > 2)
2063 	    debug("state:%d, trans:%d -> state:%d",
2064 		  mouse_button_state, trans,
2065 		  states[mouse_button_state].s[trans]);
2066 	/*
2067 	 * Avoid re-ordering button and movement events. While a button
2068 	 * event is deferred, throw away up to BUTTON2_MAXMOVE movement
2069 	 * events to allow for mouse jitter. If more movement events
2070 	 * occur, then complete the deferred button events immediately.
2071 	 */
2072 	if ((a2->dx != 0 || a2->dy != 0) &&
2073 	    S_DELAYED(states[mouse_button_state].s[trans])) {
2074 		if (++mouse_move_delayed > BUTTON2_MAXMOVE) {
2075 			mouse_move_delayed = 0;
2076 			mouse_button_state =
2077 			    states[mouse_button_state].s[A_TIMEOUT];
2078 			changed = TRUE;
2079 		} else
2080 			a2->dx = a2->dy = 0;
2081 	} else
2082 		mouse_move_delayed = 0;
2083 	if (mouse_button_state != states[mouse_button_state].s[trans])
2084 		changed = TRUE;
2085 	if (changed)
2086 		gettimeofday(&mouse_button_state_tv, NULL);
2087 	mouse_button_state = states[mouse_button_state].s[trans];
2088 	a2->button &=
2089 	    ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN);
2090 	a2->button &= states[mouse_button_state].mask;
2091 	a2->button |= states[mouse_button_state].buttons;
2092 	flags = a2->flags & MOUSE_POSCHANGED;
2093 	flags |= a2->obutton ^ a2->button;
2094 	if (flags & MOUSE_BUTTON2DOWN) {
2095 	    a2->flags = flags & MOUSE_BUTTON2DOWN;
2096 	    r_timestamp(a2);
2097 	}
2098 	a2->flags = flags;
2099     }
2100     return changed;
2101 }
2102 
2103 /* phisical to logical button mapping */
2104 static int p2l[MOUSE_MAXBUTTON] = {
2105     MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
2106     MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
2107     0x00000100,        0x00000200,        0x00000400,        0x00000800,
2108     0x00001000,        0x00002000,        0x00004000,        0x00008000,
2109     0x00010000,        0x00020000,        0x00040000,        0x00080000,
2110     0x00100000,        0x00200000,        0x00400000,        0x00800000,
2111     0x01000000,        0x02000000,        0x04000000,        0x08000000,
2112     0x10000000,        0x20000000,        0x40000000,
2113 };
2114 
2115 static char *
2116 skipspace(char *s)
2117 {
2118     while(isspace(*s))
2119 	++s;
2120     return s;
2121 }
2122 
2123 static int
2124 r_installmap(char *arg)
2125 {
2126     int pbutton;
2127     int lbutton;
2128     char *s;
2129 
2130     while (*arg) {
2131 	arg = skipspace(arg);
2132 	s = arg;
2133 	while (isdigit(*arg))
2134 	    ++arg;
2135 	arg = skipspace(arg);
2136 	if ((arg <= s) || (*arg != '='))
2137 	    return FALSE;
2138 	lbutton = atoi(s);
2139 
2140 	arg = skipspace(++arg);
2141 	s = arg;
2142 	while (isdigit(*arg))
2143 	    ++arg;
2144 	if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
2145 	    return FALSE;
2146 	pbutton = atoi(s);
2147 
2148 	if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
2149 	    return FALSE;
2150 	if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
2151 	    return FALSE;
2152 	p2l[pbutton - 1] = 1 << (lbutton - 1);
2153 	mstate[lbutton - 1] = &bstate[pbutton - 1];
2154     }
2155 
2156     return TRUE;
2157 }
2158 
2159 static void
2160 r_map(mousestatus_t *act1, mousestatus_t *act2)
2161 {
2162     register int pb;
2163     register int pbuttons;
2164     int lbuttons;
2165 
2166     pbuttons = act1->button;
2167     lbuttons = 0;
2168 
2169     act2->obutton = act2->button;
2170     if (pbuttons & rodent.wmode) {
2171 	pbuttons &= ~rodent.wmode;
2172 	act1->dz = act1->dy;
2173 	act1->dx = 0;
2174 	act1->dy = 0;
2175     }
2176     act2->dx = act1->dx;
2177     act2->dy = act1->dy;
2178     act2->dz = act1->dz;
2179 
2180     switch (rodent.zmap[0]) {
2181     case 0:	/* do nothing */
2182 	break;
2183     case MOUSE_XAXIS:
2184 	if (act1->dz != 0) {
2185 	    act2->dx = act1->dz;
2186 	    act2->dz = 0;
2187 	}
2188 	break;
2189     case MOUSE_YAXIS:
2190 	if (act1->dz != 0) {
2191 	    act2->dy = act1->dz;
2192 	    act2->dz = 0;
2193 	}
2194 	break;
2195     default:	/* buttons */
2196 	pbuttons &= ~(rodent.zmap[0] | rodent.zmap[1]
2197 		    | rodent.zmap[2] | rodent.zmap[3]);
2198 	if ((act1->dz < -1) && rodent.zmap[2]) {
2199 	    pbuttons |= rodent.zmap[2];
2200 	    zstate[2].count = 1;
2201 	} else if (act1->dz < 0) {
2202 	    pbuttons |= rodent.zmap[0];
2203 	    zstate[0].count = 1;
2204 	} else if ((act1->dz > 1) && rodent.zmap[3]) {
2205 	    pbuttons |= rodent.zmap[3];
2206 	    zstate[3].count = 1;
2207 	} else if (act1->dz > 0) {
2208 	    pbuttons |= rodent.zmap[1];
2209 	    zstate[1].count = 1;
2210 	}
2211 	act2->dz = 0;
2212 	break;
2213     }
2214 
2215     for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
2216 	lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
2217 	pbuttons >>= 1;
2218     }
2219     act2->button = lbuttons;
2220 
2221     act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
2222 	| (act2->obutton ^ act2->button);
2223 }
2224 
2225 static void
2226 r_timestamp(mousestatus_t *act)
2227 {
2228     struct timeval tv;
2229     struct timeval tv1;
2230     struct timeval tv2;
2231     struct timeval tv3;
2232     int button;
2233     int mask;
2234     int i;
2235 
2236     mask = act->flags & MOUSE_BUTTONS;
2237 #if 0
2238     if (mask == 0)
2239 	return;
2240 #endif
2241 
2242     gettimeofday(&tv1, NULL);
2243 
2244     /* double click threshold */
2245     tv2.tv_sec = rodent.clickthreshold/1000;
2246     tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
2247     timersub(&tv1, &tv2, &tv);
2248     debug("tv:  %ld %ld", tv.tv_sec, tv.tv_usec);
2249 
2250     /* 3 button emulation timeout */
2251     tv2.tv_sec = rodent.button2timeout/1000;
2252     tv2.tv_usec = (rodent.button2timeout%1000)*1000;
2253     timersub(&tv1, &tv2, &tv3);
2254 
2255     button = MOUSE_BUTTON1DOWN;
2256     for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
2257 	if (mask & 1) {
2258 	    if (act->button & button) {
2259 		/* the button is down */
2260 		debug("  :  %ld %ld",
2261 		    bstate[i].tv.tv_sec, bstate[i].tv.tv_usec);
2262 		if (timercmp(&tv, &bstate[i].tv, >)) {
2263 		    bstate[i].count = 1;
2264 		} else {
2265 		    ++bstate[i].count;
2266 		}
2267 		bstate[i].tv = tv1;
2268 	    } else {
2269 		/* the button is up */
2270 		bstate[i].tv = tv1;
2271 	    }
2272 	} else {
2273 	    if (act->button & button) {
2274 		/* the button has been down */
2275 		if (timercmp(&tv3, &bstate[i].tv, >)) {
2276 		    bstate[i].count = 1;
2277 		    bstate[i].tv = tv1;
2278 		    act->flags |= button;
2279 		    debug("button %d timeout", i + 1);
2280 		}
2281 	    } else {
2282 		/* the button has been up */
2283 	    }
2284 	}
2285 	button <<= 1;
2286 	mask >>= 1;
2287     }
2288 }
2289 
2290 static int
2291 r_timeout(void)
2292 {
2293     struct timeval tv;
2294     struct timeval tv1;
2295     struct timeval tv2;
2296 
2297     if (states[mouse_button_state].timeout)
2298 	return TRUE;
2299     gettimeofday(&tv1, NULL);
2300     tv2.tv_sec = rodent.button2timeout/1000;
2301     tv2.tv_usec = (rodent.button2timeout%1000)*1000;
2302     timersub(&tv1, &tv2, &tv);
2303     return timercmp(&tv, &mouse_button_state_tv, >);
2304 }
2305 
2306 static void
2307 r_click(mousestatus_t *act)
2308 {
2309     struct mouse_info mouse;
2310     int button;
2311     int mask;
2312     int i;
2313 
2314     mask = act->flags & MOUSE_BUTTONS;
2315     if (mask == 0)
2316 	return;
2317 
2318     button = MOUSE_BUTTON1DOWN;
2319     for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
2320 	if (mask & 1) {
2321 	    debug("mstate[%d]->count:%d", i, mstate[i]->count);
2322 	    if (act->button & button) {
2323 		/* the button is down */
2324 		mouse.u.event.value = mstate[i]->count;
2325 	    } else {
2326 		/* the button is up */
2327 		mouse.u.event.value = 0;
2328 	    }
2329 	    mouse.operation = MOUSE_BUTTON_EVENT;
2330 	    mouse.u.event.id = button;
2331 	    if (debug < 2)
2332 		ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
2333 	    debug("button %d  count %d", i + 1, mouse.u.event.value);
2334 	}
2335 	button <<= 1;
2336 	mask >>= 1;
2337     }
2338 }
2339 
2340 /* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
2341 /* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
2342 /*
2343  * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
2344  *
2345  * Permission to use, copy, modify, distribute, and sell this software and its
2346  * documentation for any purpose is hereby granted without fee, provided that
2347  * the above copyright notice appear in all copies and that both that
2348  * copyright notice and this permission notice appear in supporting
2349  * documentation, and that the name of David Dawes
2350  * not be used in advertising or publicity pertaining to distribution of
2351  * the software without specific, written prior permission.
2352  * David Dawes makes no representations about the suitability of this
2353  * software for any purpose.  It is provided "as is" without express or
2354  * implied warranty.
2355  *
2356  * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
2357  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2358  * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
2359  * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
2360  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
2361  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
2362  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2363  *
2364  */
2365 
2366 
2367 static void
2368 setmousespeed(int old, int new, unsigned cflag)
2369 {
2370 	struct termios tty;
2371 	char *c;
2372 
2373 	if (tcgetattr(rodent.mfd, &tty) < 0)
2374 	{
2375 		logwarn("unable to get status of mouse fd");
2376 		return;
2377 	}
2378 
2379 	tty.c_iflag = IGNBRK | IGNPAR;
2380 	tty.c_oflag = 0;
2381 	tty.c_lflag = 0;
2382 	tty.c_cflag = (tcflag_t)cflag;
2383 	tty.c_cc[VTIME] = 0;
2384 	tty.c_cc[VMIN] = 1;
2385 
2386 	switch (old)
2387 	{
2388 	case 9600:
2389 		cfsetispeed(&tty, B9600);
2390 		cfsetospeed(&tty, B9600);
2391 		break;
2392 	case 4800:
2393 		cfsetispeed(&tty, B4800);
2394 		cfsetospeed(&tty, B4800);
2395 		break;
2396 	case 2400:
2397 		cfsetispeed(&tty, B2400);
2398 		cfsetospeed(&tty, B2400);
2399 		break;
2400 	case 1200:
2401 	default:
2402 		cfsetispeed(&tty, B1200);
2403 		cfsetospeed(&tty, B1200);
2404 	}
2405 
2406 	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
2407 	{
2408 		logwarn("unable to set status of mouse fd");
2409 		return;
2410 	}
2411 
2412 	switch (new)
2413 	{
2414 	case 9600:
2415 		c = "*q";
2416 		cfsetispeed(&tty, B9600);
2417 		cfsetospeed(&tty, B9600);
2418 		break;
2419 	case 4800:
2420 		c = "*p";
2421 		cfsetispeed(&tty, B4800);
2422 		cfsetospeed(&tty, B4800);
2423 		break;
2424 	case 2400:
2425 		c = "*o";
2426 		cfsetispeed(&tty, B2400);
2427 		cfsetospeed(&tty, B2400);
2428 		break;
2429 	case 1200:
2430 	default:
2431 		c = "*n";
2432 		cfsetispeed(&tty, B1200);
2433 		cfsetospeed(&tty, B1200);
2434 	}
2435 
2436 	if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
2437 	    || rodent.rtype == MOUSE_PROTO_LOGI)
2438 	{
2439 		if (write(rodent.mfd, c, 2) != 2)
2440 		{
2441 			logwarn("unable to write to mouse fd");
2442 			return;
2443 		}
2444 	}
2445 	usleep(100000);
2446 
2447 	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
2448 		logwarn("unable to set status of mouse fd");
2449 }
2450 
2451 /*
2452  * PnP COM device support
2453  *
2454  * It's a simplistic implementation, but it works :-)
2455  * KY, 31/7/97.
2456  */
2457 
2458 /*
2459  * Try to elicit a PnP ID as described in
2460  * Microsoft, Hayes: "Plug and Play External COM Device Specification,
2461  * rev 1.00", 1995.
2462  *
2463  * The routine does not fully implement the COM Enumerator as par Section
2464  * 2.1 of the document.  In particular, we don't have idle state in which
2465  * the driver software monitors the com port for dynamic connection or
2466  * removal of a device at the port, because `moused' simply quits if no
2467  * device is found.
2468  *
2469  * In addition, as PnP COM device enumeration procedure slightly has
2470  * changed since its first publication, devices which follow earlier
2471  * revisions of the above spec. may fail to respond if the rev 1.0
2472  * procedure is used. XXX
2473  */
2474 static int
2475 pnpwakeup1(void)
2476 {
2477     struct timeval timeout;
2478     fd_set fds;
2479     int i;
2480 
2481     /*
2482      * This is the procedure described in rev 1.0 of PnP COM device spec.
2483      * Unfortunately, some devices which comform to earlier revisions of
2484      * the spec gets confused and do not return the ID string...
2485      */
2486     debug("PnP COM device rev 1.0 probe...");
2487 
2488     /* port initialization (2.1.2) */
2489     ioctl(rodent.mfd, TIOCMGET, &i);
2490     i |= TIOCM_DTR;		/* DTR = 1 */
2491     i &= ~TIOCM_RTS;		/* RTS = 0 */
2492     ioctl(rodent.mfd, TIOCMSET, &i);
2493     usleep(240000);
2494 
2495     /*
2496      * The PnP COM device spec. dictates that the mouse must set DSR
2497      * in response to DTR (by hardware or by software) and that if DSR is
2498      * not asserted, the host computer should think that there is no device
2499      * at this serial port.  But some mice just don't do that...
2500      */
2501     ioctl(rodent.mfd, TIOCMGET, &i);
2502     debug("modem status 0%o", i);
2503     if ((i & TIOCM_DSR) == 0)
2504 	return FALSE;
2505 
2506     /* port setup, 1st phase (2.1.3) */
2507     setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
2508     i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
2509     ioctl(rodent.mfd, TIOCMBIC, &i);
2510     usleep(240000);
2511     i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
2512     ioctl(rodent.mfd, TIOCMBIS, &i);
2513     usleep(240000);
2514 
2515     /* wait for response, 1st phase (2.1.4) */
2516     i = FREAD;
2517     ioctl(rodent.mfd, TIOCFLUSH, &i);
2518     i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
2519     ioctl(rodent.mfd, TIOCMBIS, &i);
2520 
2521     /* try to read something */
2522     FD_ZERO(&fds);
2523     FD_SET(rodent.mfd, &fds);
2524     timeout.tv_sec = 0;
2525     timeout.tv_usec = 240000;
2526     if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2527 	debug("pnpwakeup1(): valid response in first phase.");
2528 	return TRUE;
2529     }
2530 
2531     /* port setup, 2nd phase (2.1.5) */
2532     i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
2533     ioctl(rodent.mfd, TIOCMBIC, &i);
2534     usleep(240000);
2535 
2536     /* wait for respose, 2nd phase (2.1.6) */
2537     i = FREAD;
2538     ioctl(rodent.mfd, TIOCFLUSH, &i);
2539     i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
2540     ioctl(rodent.mfd, TIOCMBIS, &i);
2541 
2542     /* try to read something */
2543     FD_ZERO(&fds);
2544     FD_SET(rodent.mfd, &fds);
2545     timeout.tv_sec = 0;
2546     timeout.tv_usec = 240000;
2547     if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2548 	debug("pnpwakeup1(): valid response in second phase.");
2549 	return TRUE;
2550     }
2551 
2552     return FALSE;
2553 }
2554 
2555 static int
2556 pnpwakeup2(void)
2557 {
2558     struct timeval timeout;
2559     fd_set fds;
2560     int i;
2561 
2562     /*
2563      * This is a simplified procedure; it simply toggles RTS.
2564      */
2565     debug("alternate probe...");
2566 
2567     ioctl(rodent.mfd, TIOCMGET, &i);
2568     i |= TIOCM_DTR;		/* DTR = 1 */
2569     i &= ~TIOCM_RTS;		/* RTS = 0 */
2570     ioctl(rodent.mfd, TIOCMSET, &i);
2571     usleep(240000);
2572 
2573     setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
2574 
2575     /* wait for respose */
2576     i = FREAD;
2577     ioctl(rodent.mfd, TIOCFLUSH, &i);
2578     i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
2579     ioctl(rodent.mfd, TIOCMBIS, &i);
2580 
2581     /* try to read something */
2582     FD_ZERO(&fds);
2583     FD_SET(rodent.mfd, &fds);
2584     timeout.tv_sec = 0;
2585     timeout.tv_usec = 240000;
2586     if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2587 	debug("pnpwakeup2(): valid response.");
2588 	return TRUE;
2589     }
2590 
2591     return FALSE;
2592 }
2593 
2594 static int
2595 pnpgets(char *buf)
2596 {
2597     struct timeval timeout;
2598     fd_set fds;
2599     int begin;
2600     int i;
2601     char c;
2602 
2603     if (!pnpwakeup1() && !pnpwakeup2()) {
2604 	/*
2605 	 * According to PnP spec, we should set DTR = 1 and RTS = 0 while
2606 	 * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
2607 	 * assuming there is something at the port even if it didn't
2608 	 * respond to the PnP enumeration procedure.
2609 	 */
2610 	i = TIOCM_DTR | TIOCM_RTS;		/* DTR = 1, RTS = 1 */
2611 	ioctl(rodent.mfd, TIOCMBIS, &i);
2612 	return 0;
2613     }
2614 
2615     /* collect PnP COM device ID (2.1.7) */
2616     begin = -1;
2617     i = 0;
2618     usleep(240000);	/* the mouse must send `Begin ID' within 200msec */
2619     while (read(rodent.mfd, &c, 1) == 1) {
2620 	/* we may see "M", or "M3..." before `Begin ID' */
2621 	buf[i++] = c;
2622 	if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
2623 	    debug("begin-id %02x", c);
2624 	    begin = i - 1;
2625 	    break;
2626 	}
2627 	debug("%c %02x", c, c);
2628 	if (i >= 256)
2629 	    break;
2630     }
2631     if (begin < 0) {
2632 	/* we haven't seen `Begin ID' in time... */
2633 	goto connect_idle;
2634     }
2635 
2636     ++c;			/* make it `End ID' */
2637     for (;;) {
2638 	FD_ZERO(&fds);
2639 	FD_SET(rodent.mfd, &fds);
2640 	timeout.tv_sec = 0;
2641 	timeout.tv_usec = 240000;
2642 	if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
2643 	    break;
2644 
2645 	read(rodent.mfd, &buf[i], 1);
2646 	if (buf[i++] == c)	/* End ID */
2647 	    break;
2648 	if (i >= 256)
2649 	    break;
2650     }
2651     if (begin > 0) {
2652 	i -= begin;
2653 	bcopy(&buf[begin], &buf[0], i);
2654     }
2655     /* string may not be human readable... */
2656     debug("len:%d, '%-*.*s'", i, i, i, buf);
2657 
2658     if (buf[i - 1] == c)
2659 	return i;		/* a valid PnP string */
2660 
2661     /*
2662      * According to PnP spec, we should set DTR = 1 and RTS = 0 while
2663      * in idle state.  But, `moused' shall leave the modem control lines
2664      * as they are. See above.
2665      */
2666 connect_idle:
2667 
2668     /* we may still have something in the buffer */
2669     return ((i > 0) ? i : 0);
2670 }
2671 
2672 static int
2673 pnpparse(pnpid_t *id, char *buf, int len)
2674 {
2675     char s[3];
2676     int offset;
2677     int sum = 0;
2678     int i, j;
2679 
2680     id->revision = 0;
2681     id->eisaid = NULL;
2682     id->serial = NULL;
2683     id->class = NULL;
2684     id->compat = NULL;
2685     id->description = NULL;
2686     id->neisaid = 0;
2687     id->nserial = 0;
2688     id->nclass = 0;
2689     id->ncompat = 0;
2690     id->ndescription = 0;
2691 
2692     if ((buf[0] != 0x28) && (buf[0] != 0x08)) {
2693 	/* non-PnP mice */
2694 	switch(buf[0]) {
2695 	default:
2696 	    return FALSE;
2697 	case 'M': /* Microsoft */
2698 	    id->eisaid = "PNP0F01";
2699 	    break;
2700 	case 'H': /* MouseSystems */
2701 	    id->eisaid = "PNP0F04";
2702 	    break;
2703 	}
2704 	id->neisaid = strlen(id->eisaid);
2705 	id->class = "MOUSE";
2706 	id->nclass = strlen(id->class);
2707 	debug("non-PnP mouse '%c'", buf[0]);
2708 	return TRUE;
2709     }
2710 
2711     /* PnP mice */
2712     offset = 0x28 - buf[0];
2713 
2714     /* calculate checksum */
2715     for (i = 0; i < len - 3; ++i) {
2716 	sum += buf[i];
2717 	buf[i] += offset;
2718     }
2719     sum += buf[len - 1];
2720     for (; i < len; ++i)
2721 	buf[i] += offset;
2722     debug("PnP ID string: '%*.*s'", len, len, buf);
2723 
2724     /* revision */
2725     buf[1] -= offset;
2726     buf[2] -= offset;
2727     id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
2728     debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
2729 
2730     /* EISA vender and product ID */
2731     id->eisaid = &buf[3];
2732     id->neisaid = 7;
2733 
2734     /* option strings */
2735     i = 10;
2736     if (buf[i] == '\\') {
2737 	/* device serial # */
2738 	for (j = ++i; i < len; ++i) {
2739 	    if (buf[i] == '\\')
2740 		break;
2741 	}
2742 	if (i >= len)
2743 	    i -= 3;
2744 	if (i - j == 8) {
2745 	    id->serial = &buf[j];
2746 	    id->nserial = 8;
2747 	}
2748     }
2749     if (buf[i] == '\\') {
2750 	/* PnP class */
2751 	for (j = ++i; i < len; ++i) {
2752 	    if (buf[i] == '\\')
2753 		break;
2754 	}
2755 	if (i >= len)
2756 	    i -= 3;
2757 	if (i > j + 1) {
2758 	    id->class = &buf[j];
2759 	    id->nclass = i - j;
2760 	}
2761     }
2762     if (buf[i] == '\\') {
2763 	/* compatible driver */
2764 	for (j = ++i; i < len; ++i) {
2765 	    if (buf[i] == '\\')
2766 		break;
2767 	}
2768 	/*
2769 	 * PnP COM spec prior to v0.96 allowed '*' in this field,
2770 	 * it's not allowed now; just igore it.
2771 	 */
2772 	if (buf[j] == '*')
2773 	    ++j;
2774 	if (i >= len)
2775 	    i -= 3;
2776 	if (i > j + 1) {
2777 	    id->compat = &buf[j];
2778 	    id->ncompat = i - j;
2779 	}
2780     }
2781     if (buf[i] == '\\') {
2782 	/* product description */
2783 	for (j = ++i; i < len; ++i) {
2784 	    if (buf[i] == ';')
2785 		break;
2786 	}
2787 	if (i >= len)
2788 	    i -= 3;
2789 	if (i > j + 1) {
2790 	    id->description = &buf[j];
2791 	    id->ndescription = i - j;
2792 	}
2793     }
2794 
2795     /* checksum exists if there are any optional fields */
2796     if ((id->nserial > 0) || (id->nclass > 0)
2797 	|| (id->ncompat > 0) || (id->ndescription > 0)) {
2798 	debug("PnP checksum: 0x%X", sum);
2799 	sprintf(s, "%02X", sum & 0x0ff);
2800 	if (strncmp(s, &buf[len - 3], 2) != 0) {
2801 #if 0
2802 	    /*
2803 	     * I found some mice do not comply with the PnP COM device
2804 	     * spec regarding checksum... XXX
2805 	     */
2806 	    logwarnx("PnP checksum error", 0);
2807 	    return FALSE;
2808 #endif
2809 	}
2810     }
2811 
2812     return TRUE;
2813 }
2814 
2815 static symtab_t *
2816 pnpproto(pnpid_t *id)
2817 {
2818     symtab_t *t;
2819     int i, j;
2820 
2821     if (id->nclass > 0)
2822 	if (strncmp(id->class, "MOUSE", id->nclass) != 0 &&
2823 	    strncmp(id->class, "TABLET", id->nclass) != 0)
2824 	    /* this is not a mouse! */
2825 	    return NULL;
2826 
2827     if (id->neisaid > 0) {
2828 	t = gettoken(pnpprod, id->eisaid, id->neisaid);
2829 	if (t->val != MOUSE_PROTO_UNKNOWN)
2830 	    return t;
2831     }
2832 
2833     /*
2834      * The 'Compatible drivers' field may contain more than one
2835      * ID separated by ','.
2836      */
2837     if (id->ncompat <= 0)
2838 	return NULL;
2839     for (i = 0; i < id->ncompat; ++i) {
2840 	for (j = i; id->compat[i] != ','; ++i)
2841 	    if (i >= id->ncompat)
2842 		break;
2843 	if (i > j) {
2844 	    t = gettoken(pnpprod, id->compat + j, i - j);
2845 	    if (t->val != MOUSE_PROTO_UNKNOWN)
2846 		return t;
2847 	}
2848     }
2849 
2850     return NULL;
2851 }
2852 
2853 /* name/val mapping */
2854 
2855 static symtab_t *
2856 gettoken(symtab_t *tab, char *s, int len)
2857 {
2858     int i;
2859 
2860     for (i = 0; tab[i].name != NULL; ++i) {
2861 	if (strncmp(tab[i].name, s, len) == 0)
2862 	    break;
2863     }
2864     return &tab[i];
2865 }
2866 
2867 static char *
2868 gettokenname(symtab_t *tab, int val)
2869 {
2870     int i;
2871 
2872     for (i = 0; tab[i].name != NULL; ++i) {
2873 	if (tab[i].val == val)
2874 	    return tab[i].name;
2875     }
2876     return NULL;
2877 }
2878 
2879 
2880 /*
2881  * code to read from the Genius Kidspad tablet.
2882 
2883 The tablet responds to the COM PnP protocol 1.0 with EISA-ID KYE0005,
2884 and to pre-pnp probes (RTS toggle) with 'T' (tablet ?)
2885 9600, 8 bit, parity odd.
2886 
2887 The tablet puts out 5 bytes. b0 (mask 0xb8, value 0xb8) contains
2888 the proximity, tip and button info:
2889    (byte0 & 0x1)	true = tip pressed
2890    (byte0 & 0x2)	true = button pressed
2891    (byte0 & 0x40)	false = pen in proximity of tablet.
2892 
2893 The next 4 bytes are used for coordinates xl, xh, yl, yh (7 bits valid).
2894 
2895 Only absolute coordinates are returned, so we use the following approach:
2896 we store the last coordinates sent when the pen went out of the tablet,
2897 
2898 
2899  *
2900  */
2901 
2902 typedef enum {
2903     S_IDLE, S_PROXY, S_FIRST, S_DOWN, S_UP
2904 } k_status ;
2905 
2906 static int
2907 kidspad(u_char rxc, mousestatus_t *act)
2908 {
2909     static int buf[5];
2910     static int buflen = 0, b_prev = 0 , x_prev = -1, y_prev = -1 ;
2911     static k_status status = S_IDLE ;
2912     static struct timeval old, now ;
2913 
2914     int x, y ;
2915 
2916     if (buflen > 0 && (rxc & 0x80)) {
2917 	fprintf(stderr, "invalid code %d 0x%x\n", buflen, rxc);
2918 	buflen = 0 ;
2919     }
2920     if (buflen == 0 && (rxc & 0xb8) != 0xb8) {
2921 	fprintf(stderr, "invalid code 0 0x%x\n", rxc);
2922 	return 0 ; /* invalid code, no action */
2923     }
2924     buf[buflen++] = rxc ;
2925     if (buflen < 5)
2926 	return 0 ;
2927 
2928     buflen = 0 ; /* for next time... */
2929 
2930     x = buf[1]+128*(buf[2] - 7) ;
2931     if (x < 0) x = 0 ;
2932     y = 28*128 - (buf[3] + 128* (buf[4] - 7)) ;
2933     if (y < 0) y = 0 ;
2934 
2935     x /= 8 ;
2936     y /= 8 ;
2937 
2938     act->flags = 0 ;
2939     act->obutton = act->button ;
2940     act->dx = act->dy = act->dz = 0 ;
2941     gettimeofday(&now, NULL);
2942     if (buf[0] & 0x40) /* pen went out of reach */
2943 	status = S_IDLE ;
2944     else if (status == S_IDLE) { /* pen is newly near the tablet */
2945 	act->flags |= MOUSE_POSCHANGED ; /* force update */
2946 	status = S_PROXY ;
2947 	x_prev = x ;
2948 	y_prev = y ;
2949     }
2950     old = now ;
2951     act->dx = x - x_prev ;
2952     act->dy = y - y_prev ;
2953     if (act->dx || act->dy)
2954 	act->flags |= MOUSE_POSCHANGED ;
2955     x_prev = x ;
2956     y_prev = y ;
2957     if (b_prev != 0 && b_prev != buf[0]) { /* possibly record button change */
2958 	act->button = 0 ;
2959 	if (buf[0] & 0x01) /* tip pressed */
2960 	    act->button |= MOUSE_BUTTON1DOWN ;
2961 	if (buf[0] & 0x02) /* button pressed */
2962 	    act->button |= MOUSE_BUTTON2DOWN ;
2963 	act->flags |= MOUSE_BUTTONSCHANGED ;
2964     }
2965     b_prev = buf[0] ;
2966     return act->flags ;
2967 }
2968 
2969 static void
2970 mremote_serversetup()
2971 {
2972     struct sockaddr_un ad;
2973 
2974     /* Open a UNIX domain stream socket to listen for mouse remote clients */
2975     unlink(_PATH_MOUSEREMOTE);
2976 
2977     if ((rodent.mremsfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
2978 	logerrx(1, "unable to create unix domain socket %s",_PATH_MOUSEREMOTE);
2979 
2980     umask(0111);
2981 
2982     bzero(&ad, sizeof(ad));
2983     ad.sun_family = AF_UNIX;
2984     strcpy(ad.sun_path, _PATH_MOUSEREMOTE);
2985 #ifndef SUN_LEN
2986 #define SUN_LEN(unp) (((char *)(unp)->sun_path - (char *)(unp)) + \
2987 		       strlen((unp)->path))
2988 #endif
2989     if (bind(rodent.mremsfd, (struct sockaddr *) &ad, SUN_LEN(&ad)) < 0)
2990 	logerrx(1, "unable to bind unix domain socket %s", _PATH_MOUSEREMOTE);
2991 
2992     listen(rodent.mremsfd, 1);
2993 }
2994 
2995 static void
2996 mremote_clientchg(int add)
2997 {
2998     struct sockaddr_un ad;
2999     int ad_len, fd;
3000 
3001     if (rodent.rtype != MOUSE_PROTO_X10MOUSEREM)
3002 	return;
3003 
3004     if (add) {
3005 	/*  Accept client connection, if we don't already have one  */
3006 	ad_len = sizeof(ad);
3007 	fd = accept(rodent.mremsfd, (struct sockaddr *) &ad, &ad_len);
3008 	if (fd < 0)
3009 	    logwarnx("failed accept on mouse remote socket");
3010 
3011 	if (rodent.mremcfd < 0) {
3012 	    rodent.mremcfd = fd;
3013 	    debug("remote client connect...accepted");
3014 	}
3015 	else {
3016 	    close(fd);
3017 	    debug("another remote client connect...disconnected");
3018 	}
3019     }
3020     else {
3021 	/* Client disconnected */
3022 	debug("remote client disconnected");
3023 	close(rodent.mremcfd);
3024 	rodent.mremcfd = -1;
3025     }
3026 }
3027