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