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