moused.c revision 31726
123678Speter/**
250476Speter ** Copyright (c) 1995 Michael Smith, All rights reserved.
31558Srgrimes **
41558Srgrimes ** Redistribution and use in source and binary forms, with or without
5152738Srodrigc ** modification, are permitted provided that the following conditions
6152344Srodrigc ** are met:
774815Sru ** 1. Redistributions of source code must retain the above copyright
81558Srgrimes **    notice, this list of conditions and the following disclaimer as
91558Srgrimes **    the first lines of this file unmodified.
101558Srgrimes ** 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	"$Id: moused.c,v 1.11 1997/12/07 08:11:16 yokota Exp $";
50#endif /* not lint */
51
52#include <err.h>
53#include <errno.h>
54#include <fcntl.h>
55#include <limits.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <stdarg.h>
59#include <string.h>
60#include <ctype.h>
61#include <signal.h>
62#include <setjmp.h>
63#include <termios.h>
64#include <syslog.h>
65
66#include <machine/console.h>
67#include <machine/mouse.h>
68
69#include <sys/types.h>
70#include <sys/time.h>
71#include <unistd.h>
72
73#define MAX_CLICKTHRESHOLD	2000	/* 2 seconds */
74
75#define TRUE		1
76#define FALSE		0
77
78#define MOUSE_XAXIS	(-1)
79#define MOUSE_YAXIS	(-2)
80
81#define	ChordMiddle	0x0001
82#define Emulate3Button	0x0002
83#define ClearDTR	0x0004
84#define ClearRTS	0x0008
85#define NoPnP		0x0010
86
87#define ID_NONE		0
88#define ID_PORT		1
89#define ID_IF		2
90#define ID_TYPE 	4
91#define ID_MODEL	8
92#define ID_ALL		(ID_PORT | ID_IF | ID_TYPE | ID_MODEL)
93
94#define debug(fmt,args...) \
95	if (debug&&nodaemon) warnx(fmt, ##args)
96
97#define logerr(e, fmt, args...) {				\
98	if (background) {					\
99	    syslog(LOG_DAEMON | LOG_ERR, fmt ": %m", ##args);	\
100	    exit(e);						\
101	} else							\
102	    err(e, fmt, ##args);				\
103}
104
105#define logerrx(e, fmt, args...) {				\
106	if (background) {					\
107	    syslog(LOG_DAEMON | LOG_ERR, fmt, ##args);		\
108	    exit(e);						\
109	} else							\
110	    errx(e, fmt, ##args);				\
111}
112
113#define logwarn(fmt, args...) {					\
114	if (background)						\
115	    syslog(LOG_DAEMON | LOG_WARNING, fmt ": %m", ##args); \
116	else							\
117	    warn(fmt, ##args);					\
118}
119
120#define logwarnx(fmt, args...) {				\
121	if (background)						\
122	    syslog(LOG_DAEMON | LOG_WARNING, fmt, ##args);	\
123	else							\
124	    warnx(fmt, ##args);					\
125}
126
127/* structures */
128
129/* symbol table entry */
130typedef struct {
131    char *name;
132    int val;
133    int val2;
134} symtab_t;
135
136/* serial PnP ID string */
137typedef struct {
138    int revision;	/* PnP revision, 100 for 1.00 */
139    char *eisaid;	/* EISA ID including mfr ID and product ID */
140    char *serial;	/* serial No, optional */
141    char *class;	/* device class, optional */
142    char *compat;	/* list of compatible drivers, optional */
143    char *description;	/* product description, optional */
144    int neisaid;	/* length of the above fields... */
145    int nserial;
146    int nclass;
147    int ncompat;
148    int ndescription;
149} pnpid_t;
150
151/* global variables */
152
153int	debug = 0;
154int	nodaemon = FALSE;
155int	background = FALSE;
156int	identify = ID_NONE;
157int	extioctl = FALSE;
158
159/* local variables */
160
161/* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
162static symtab_t rifs[] = {
163    { "serial",		MOUSE_IF_SERIAL },
164    { "bus",		MOUSE_IF_BUS },
165    { "inport",		MOUSE_IF_INPORT },
166    { "ps/2",		MOUSE_IF_PS2 },
167    { "sysmouse",	MOUSE_IF_SYSMOUSE },
168    { NULL,		MOUSE_IF_UNKNOWN },
169};
170
171/* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
172static char *rnames[] = {
173    "microsoft",
174    "mousesystems",
175    "logitech",
176    "mmseries",
177    "mouseman",
178    "busmouse",
179    "inportmouse",
180    "ps/2",
181    "mmhitab",
182    "glidepoint",
183    "intellimouse",
184    "thinkingmouse",
185    "sysmouse",
186#if notyet
187    "mariqua",
188#endif
189    NULL
190};
191
192/* models */
193static symtab_t	rmodels[] = {
194    { "NetScroll",	MOUSE_MODEL_NETSCROLL },
195    { "NetMouse",	MOUSE_MODEL_NET },
196    { "GlidePoint",	MOUSE_MODEL_GLIDEPOINT },
197    { "ThinkingMouse",	MOUSE_MODEL_THINK },
198    { "IntelliMouse",	MOUSE_MODEL_INTELLI },
199    { "EasyScroll",	MOUSE_MODEL_EASYSCROLL },
200    { "MouseMan+",	MOUSE_MODEL_MOUSEMANPLUS },
201    { "generic",	MOUSE_MODEL_GENERIC },
202    { NULL, 		MOUSE_MODEL_UNKNOWN },
203};
204
205/* PnP EISA/product IDs */
206static symtab_t pnpprod[] = {
207    /* Kensignton ThinkingMouse */
208    { "KML0001",	MOUSE_PROTO_THINK,	MOUSE_MODEL_THINK },
209    /* MS IntelliMouse */
210    { "MSH0001",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
211    /* MS IntelliMouse TrackBall */
212    { "MSH0004",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_INTELLI },
213    /* Genius EZScroll */
214    { "KYEEZ00",	MOUSE_PROTO_MS,		MOUSE_MODEL_EASYSCROLL },
215    /* Genius NetMouse */
216    { "KYE0003",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_NET },
217    /* Logitech MouseMan+ */
218    { "LGI8050",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
219    /* Logitech FirstMouse+ */
220    { "LGI8051",	MOUSE_PROTO_INTELLI,	MOUSE_MODEL_MOUSEMANPLUS },
221
222    /* MS bus */
223    { "PNP0F00",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
224    /* MS serial */
225    { "PNP0F01",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
226    /* MS InPort */
227    { "PNP0F02",	MOUSE_PROTO_INPORT,	MOUSE_MODEL_GENERIC },
228    /* MS PS/2 */
229    { "PNP0F03",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
230    /*
231     * EzScroll returns PNP0F04 in the compatible device field; but it
232     * doesn't look compatible... XXX
233     */
234    /* MouseSystems */
235    { "PNP0F04",	MOUSE_PROTO_MSC,	MOUSE_MODEL_GENERIC },
236    /* MouseSystems */
237    { "PNP0F05",	MOUSE_PROTO_MSC,	MOUSE_MODEL_GENERIC },
238#if notyet
239    /* Genius Mouse */
240    { "PNP0F06",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
241    /* Genius Mouse */
242    { "PNP0F07",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
243#endif
244    /* Logitech serial */
245    { "PNP0F08",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
246    /* MS BallPoint serial */
247    { "PNP0F09",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
248    /* MS PnP serial */
249    { "PNP0F0A",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
250    /* MS PnP BallPoint serial */
251    { "PNP0F0B",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
252    /* MS serial comatible */
253    { "PNP0F0C",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
254    /* MS InPort comatible */
255    { "PNP0F0D",	MOUSE_PROTO_INPORT,	MOUSE_MODEL_GENERIC },
256    /* MS PS/2 comatible */
257    { "PNP0F0E",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
258    /* MS BallPoint comatible */
259    { "PNP0F0F",	MOUSE_PROTO_MS,		MOUSE_MODEL_GENERIC },
260#if notyet
261    /* TI QuickPort */
262    { "PNP0F10",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
263#endif
264    /* MS bus comatible */
265    { "PNP0F11",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
266    /* Logitech PS/2 */
267    { "PNP0F12",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
268    /* PS/2 */
269    { "PNP0F13",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
270#if notyet
271    /* MS Kids Mouse */
272    { "PNP0F14",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
273#endif
274    /* Logitech bus */
275    { "PNP0F15",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
276#if notyet
277    /* Logitech SWIFT */
278    { "PNP0F16",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
279#endif
280    /* Logitech serial compat */
281    { "PNP0F17",	MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
282    /* Logitech bus compatible */
283    { "PNP0F18",	MOUSE_PROTO_BUS,	MOUSE_MODEL_GENERIC },
284    /* Logitech PS/2 compatible */
285    { "PNP0F19",	MOUSE_PROTO_PS2,	MOUSE_MODEL_GENERIC },
286#if notyet
287    /* Logitech SWIFT compatible */
288    { "PNP0F1A",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
289    /* HP Omnibook */
290    { "PNP0F1B",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
291    /* Compaq LTE TrackBall PS/2 */
292    { "PNP0F1C",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
293    /* Compaq LTE TrackBall serial */
294    { "PNP0F1D",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
295    /* MS Kidts Trackball */
296    { "PNP0F1E",	MOUSE_PROTO_???,	MOUSE_MODEL_GENERIC },
297#endif
298
299    { NULL,		MOUSE_PROTO_UNKNOWN,	MOUSE_MODEL_GENERIC },
300};
301
302/* the table must be ordered by MOUSE_PROTO_XXX in mouse.h */
303static unsigned short rodentcflags[] =
304{
305    (CS7	           | CREAD | CLOCAL | HUPCL ),	/* MicroSoft */
306    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* MouseSystems */
307    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* Logitech */
308    (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ),	/* MMSeries */
309    (CS7		   | CREAD | CLOCAL | HUPCL ),	/* MouseMan */
310    0,							/* Bus */
311    0,							/* InPort */
312    0,							/* PS/2 */
313    (CS8		   | CREAD | CLOCAL | HUPCL ),	/* MM HitTablet */
314    (CS7	           | CREAD | CLOCAL | HUPCL ),	/* GlidePoint */
315    (CS7                   | CREAD | CLOCAL | HUPCL ),	/* IntelliMouse */
316    (CS7                   | CREAD | CLOCAL | HUPCL ),	/* Thinking Mouse */
317    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* sysmouse */
318#if notyet
319    (CS8 | CSTOPB	   | CREAD | CLOCAL | HUPCL ),	/* Mariqua */
320#endif
321};
322
323static struct rodentparam {
324    int flags;
325    char *portname;		/* /dev/XXX */
326    int rtype;			/* MOUSE_PROTO_XXX */
327    int level;			/* operation level: 0 or greater */
328    int baudrate;
329    int rate;			/* report rate */
330    int resolution;		/* MOUSE_RES_XXX or a positive number */
331    int zmap;			/* MOUSE_{X|Y}AXIS or a button number */
332    int mfd;			/* mouse file descriptor */
333    int cfd;			/* /dev/consolectl file descriptor */
334    long clickthreshold;	/* double click speed in msec */
335    mousehw_t hw;		/* mouse device hardware information */
336    mousemode_t mode;		/* protocol information */
337} rodent = {
338    flags : 0,
339    portname : NULL,
340    rtype : MOUSE_PROTO_UNKNOWN,
341    level : -1,
342    baudrate : 1200,
343    rate : 0,
344    resolution : MOUSE_RES_UNKNOWN,
345    zmap: 0,
346    mfd : -1,
347    cfd : -1,
348    clickthreshold : 500,	/* 0.5 sec */
349};
350
351/* button status */
352static struct {
353    int count;		/* 0: up, 1: single click, 2: double click,... */
354    struct timeval tv;	/* timestamp on the last `up' event */
355} buttonstate[MOUSE_MAXBUTTON];
356
357static jmp_buf env;
358
359/* function prototypes */
360
361static void	moused(void);
362static void	hup(int sig);
363static void	usage(void);
364
365static int	r_identify(void);
366static char	*r_if(int type);
367static char	*r_name(int type);
368static char	*r_model(int model);
369static void	r_init(void);
370static int	r_protocol(u_char b, mousestatus_t *act);
371static int	r_installmap(char *arg);
372static void	r_map(mousestatus_t *act1, mousestatus_t *act2);
373static void	r_click(mousestatus_t *act);
374static void	setmousespeed(int old, int new, unsigned cflag);
375
376static int	pnpgets(char *buf);
377static int	pnpparse(pnpid_t *id, char *buf, int len);
378static symtab_t	*pnpproto(pnpid_t *id);
379
380static symtab_t	*gettoken(symtab_t *tab, char *s, int len);
381static char	*gettokenname(symtab_t *tab, int val);
382
383void
384main(int argc, char *argv[])
385{
386    int c;
387    int	i;
388
389    while((c = getopt(argc,argv,"3C:DF:PRS:cdfhi:l:m:p:r:st:z:")) != -1)
390	switch(c) {
391
392	case '3':
393	    rodent.flags |= Emulate3Button;
394	    break;
395
396	case 'c':
397	    rodent.flags |= ChordMiddle;
398	    break;
399
400	case 'd':
401	    ++debug;
402	    break;
403
404	case 'f':
405	    nodaemon = TRUE;
406	    break;
407
408	case 'i':
409	    if (strcmp(optarg, "all") == 0)
410	        identify = ID_ALL;
411	    else if (strcmp(optarg, "port") == 0)
412	        identify = ID_PORT;
413	    else if (strcmp(optarg, "if") == 0)
414	        identify = ID_IF;
415	    else if (strcmp(optarg, "type") == 0)
416	        identify = ID_TYPE;
417	    else if (strcmp(optarg, "model") == 0)
418	        identify = ID_MODEL;
419	    else {
420	        warnx("invalid argument `%s'", optarg);
421	        usage();
422	    }
423	    nodaemon = TRUE;
424	    break;
425
426	case 'l':
427	    rodent.level = atoi(optarg);
428	    if ((rodent.level < 0) || (rodent.level > 4)) {
429	        warnx("invalid argument `%s'", optarg);
430	        usage();
431	    }
432	    break;
433
434	case 'm':
435	    if (!r_installmap(optarg)) {
436	        warnx("invalid argument `%s'", optarg);
437	        usage();
438	    }
439	    break;
440
441	case 'p':
442	    rodent.portname = optarg;
443	    break;
444
445	case 'r':
446	    if (strcmp(optarg, "high") == 0)
447	        rodent.resolution = MOUSE_RES_HIGH;
448	    else if (strcmp(optarg, "medium-high") == 0)
449	        rodent.resolution = MOUSE_RES_HIGH;
450	    else if (strcmp(optarg, "medium-low") == 0)
451	        rodent.resolution = MOUSE_RES_MEDIUMLOW;
452	    else if (strcmp(optarg, "low") == 0)
453	        rodent.resolution = MOUSE_RES_LOW;
454	    else if (strcmp(optarg, "default") == 0)
455	        rodent.resolution = MOUSE_RES_DEFAULT;
456	    else {
457	        rodent.resolution = atoi(optarg);
458	        if (rodent.resolution <= 0) {
459	            warnx("invalid argument `%s'", optarg);
460	            usage();
461	        }
462	    }
463	    break;
464
465	case 's':
466	    rodent.baudrate = 9600;
467	    break;
468
469	case 'z':
470	    if (strcmp(optarg, "x") == 0)
471		rodent.zmap = MOUSE_XAXIS;
472	    else if (strcmp(optarg, "y") == 0)
473		rodent.zmap = MOUSE_YAXIS;
474            else {
475		i = atoi(optarg);
476		/*
477		 * Use button i for negative Z axis movement and
478		 * button (i + 1) for positive Z axis movement.
479		 */
480		if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
481	            warnx("invalid argument `%s'", optarg);
482	            usage();
483		}
484		rodent.zmap = 1 << (i - 1);
485	    }
486	    break;
487
488	case 'C':
489	    rodent.clickthreshold = atoi(optarg);
490	    if ((rodent.clickthreshold < 0) ||
491	        (rodent.clickthreshold > MAX_CLICKTHRESHOLD)) {
492	        warnx("invalid argument `%s'", optarg);
493	        usage();
494	    }
495	    break;
496
497	case 'D':
498	    rodent.flags |= ClearDTR;
499	    break;
500
501	case 'F':
502	    rodent.rate = atoi(optarg);
503	    if (rodent.rate <= 0) {
504	        warnx("invalid argument `%s'", optarg);
505	        usage();
506	    }
507	    break;
508
509	case 'P':
510	    rodent.flags |= NoPnP;
511	    break;
512
513	case 'R':
514	    rodent.flags |= ClearRTS;
515	    break;
516
517	case 'S':
518	    rodent.baudrate = atoi(optarg);
519	    if (rodent.baudrate <= 0) {
520	        warnx("invalid argument `%s'", optarg);
521	        usage();
522	    }
523	    debug("rodent baudrate %d", rodent.baudrate);
524	    break;
525
526	case 't':
527	    for (i = 0; rnames[i]; i++)
528		if (!strcmp(optarg,rnames[i])) {
529		    rodent.rtype = i;
530		    break;
531		}
532	    if (rnames[i])
533		break;
534	    warnx("no such mouse type `%s'", optarg);
535	    usage();
536
537	case 'h':
538	case '?':
539	default:
540	    usage();
541	}
542
543    /* the default port name */
544    switch(rodent.rtype) {
545
546    case MOUSE_PROTO_INPORT:
547        /* INPORT and BUS are the same... */
548	rodent.rtype = MOUSE_PROTO_BUS;
549	/* FALL THROUGH */
550    case MOUSE_PROTO_BUS:
551	if (!rodent.portname)
552	    rodent.portname = "/dev/mse0";
553	break;
554
555    case MOUSE_PROTO_PS2:
556	if (!rodent.portname)
557	    rodent.portname = "/dev/psm0";
558	break;
559
560    default:
561	if (rodent.portname)
562	    break;
563	warnx("no port name specified");
564	usage();
565    }
566
567    for (;;) {
568	if (setjmp(env) == 0) {
569	    signal(SIGHUP, hup);
570            if ((rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK, 0))
571		== -1)
572	        logerr(1, "unable to open %s", rodent.portname);
573            if (r_identify() == MOUSE_PROTO_UNKNOWN) {
574	        logwarnx("cannot determine mouse type on %s", rodent.portname);
575	        close(rodent.mfd);
576	        rodent.mfd = -1;
577            }
578
579	    /* print some information */
580            if (identify != ID_NONE) {
581		if (identify == ID_ALL)
582                    printf("%s %s %s %s\n",
583		        rodent.portname, r_if(rodent.hw.iftype),
584		        r_name(rodent.rtype), r_model(rodent.hw.model));
585		else if (identify & ID_PORT)
586		    printf("%s\n", rodent.portname);
587		else if (identify & ID_IF)
588		    printf("%s\n", r_if(rodent.hw.iftype));
589		else if (identify & ID_TYPE)
590		    printf("%s\n", r_name(rodent.rtype));
591		else if (identify & ID_MODEL)
592		    printf("%s\n", r_model(rodent.hw.model));
593		exit(0);
594	    } else {
595                debug("port: %s  interface: %s  type: %s  model: %s",
596		    rodent.portname, r_if(rodent.hw.iftype),
597		    r_name(rodent.rtype), r_model(rodent.hw.model));
598	    }
599
600	    if (rodent.mfd == -1) {
601	        /*
602	         * We cannot continue because of error.  Exit if the
603		 * program has not become a daemon.  Otherwise, block
604		 * until the the user corrects the problem and issues SIGHUP.
605	         */
606	        if (!background)
607		    exit(1);
608	        sigpause(0);
609	    }
610
611            r_init();			/* call init function */
612	    moused();
613	}
614
615	if (rodent.mfd != -1)
616	    close(rodent.mfd);
617	if (rodent.cfd != -1)
618	    close(rodent.cfd);
619	rodent.mfd = rodent.cfd = -1;
620    }
621    /* NOT REACHED */
622
623    exit(0);
624}
625
626static void
627moused(void)
628{
629    struct mouse_info mouse;
630    mousestatus_t action;		/* original mouse action */
631    mousestatus_t action2;		/* mapped action */
632    fd_set fds;
633    u_char b;
634
635    if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
636	logerr(1, "cannot open /dev/consolectl", 0);
637
638    if (!nodaemon && !background)
639	if (daemon(0, 0)) {
640	    logerr(1, "failed to become a daemon", 0);
641	} else {
642	    background = TRUE;
643	}
644
645    /* clear mouse data */
646    bzero(&action, sizeof(action));
647    bzero(&action2, sizeof(action2));
648    bzero(&buttonstate, sizeof(buttonstate));
649    bzero(&mouse, sizeof(mouse));
650
651    /* choose which ioctl command to use */
652    mouse.operation = MOUSE_MOTION_EVENT;
653    extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
654
655    /* process mouse data */
656    for (;;) {
657
658	FD_ZERO(&fds);
659	FD_SET(rodent.mfd, &fds);
660	if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
661	    logwarn("failed to read from mouse", 0);
662
663	read(rodent.mfd, &b, 1);
664	if (r_protocol(b, &action)) {	/* handler detected action */
665	    r_map(&action, &action2);
666	    debug("activity : buttons 0x%08x  dx %d  dy %d  dz %d",
667		action2.button, action2.dx, action2.dy, action2.dz);
668
669	    if (extioctl) {
670	        r_click(&action2);
671	        if (action2.flags & MOUSE_POSCHANGED) {
672    		    mouse.operation = MOUSE_MOTION_EVENT;
673	            mouse.u.data.buttons = action2.button;
674	            mouse.u.data.x = action2.dx;
675	            mouse.u.data.y = action2.dy;
676	            mouse.u.data.z = action2.dz;
677		    if (debug < 2)
678	                ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
679	        }
680	    } else {
681	        mouse.operation = MOUSE_ACTION;
682	        mouse.u.data.buttons = action2.button;
683	        mouse.u.data.x = action2.dx;
684	        mouse.u.data.y = action2.dy;
685	        mouse.u.data.z = action2.dz;
686		if (debug < 2)
687	            ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
688	    }
689
690            /*
691	     * If the Z axis movement is mapped to a imaginary physical
692	     * button, we need to cook up a corresponding button `up' event
693	     * after sending a button `down' event.
694	     */
695            if ((rodent.zmap > 0) && (action.dz != 0)) {
696		action.obutton = action.button;
697		action.dx = action.dy = action.dz = 0;
698	        r_map(&action, &action2);
699	        debug("activity : buttons 0x%08x  dx %d  dy %d  dz %d",
700		    action2.button, action2.dx, action2.dy, action2.dz);
701
702	        if (extioctl) {
703	            r_click(&action2);
704	        } else {
705	            mouse.operation = MOUSE_ACTION;
706	            mouse.u.data.buttons = action2.button;
707		    mouse.u.data.x = mouse.u.data.y = mouse.u.data.z = 0;
708		    if (debug < 2)
709	                ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
710	        }
711	    }
712	}
713    }
714    /* NOT REACHED */
715}
716
717static void
718hup(int sig)
719{
720    longjmp(env, 1);
721}
722
723/**
724 ** usage
725 **
726 ** Complain, and free the CPU for more worthy tasks
727 **/
728static void
729usage(void)
730{
731    fprintf(stderr, "%s\n%s\n%s\n",
732        "usage: moused [-3DRcdfs] [-F rate] [-r resolution] [-S baudrate] [-C threshold]",
733        "              [-m N=M] [-z N] [-t <mousetype>] -p <port>",
734	"       moused [-d] -i -p <port>");
735    exit(1);
736}
737
738/**
739 ** Mouse interface code, courtesy of XFree86 3.1.2.
740 **
741 ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
742 ** to clean, reformat and rationalise naming, it's quite possible that
743 ** some things in here have been broken.
744 **
745 ** I hope not 8)
746 **
747 ** The following code is derived from a module marked :
748 **/
749
750/* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
751/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
752 17:03:40 dawes Exp $ */
753/*
754 *
755 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
756 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
757 *
758 * Permission to use, copy, modify, distribute, and sell this software and its
759 * documentation for any purpose is hereby granted without fee, provided that
760 * the above copyright notice appear in all copies and that both that
761 * copyright notice and this permission notice appear in supporting
762 * documentation, and that the names of Thomas Roell and David Dawes not be
763 * used in advertising or publicity pertaining to distribution of the
764 * software without specific, written prior permission.  Thomas Roell
765 * and David Dawes makes no representations about the suitability of this
766 * software for any purpose.  It is provided "as is" without express or
767 * implied warranty.
768 *
769 * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
770 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
771 * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
772 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
773 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
774 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
775 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
776 *
777 */
778
779/**
780 ** GlidePoint support from XFree86 3.2.
781 ** Derived from the module:
782 **/
783
784/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.19 1996/10/16 14:40:51 dawes Exp $ */
785/* $XConsortium: xf86_Mouse.c /main/10 1996/01/30 15:16:12 kaleb $ */
786
787/* the following table must be ordered by MOUSE_PROTO_XXX in mouse.h */
788static unsigned char proto[][7] = {
789    /*  hd_mask hd_id   dp_mask dp_id   bytes b4_mask b4_id */
790    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x23,  0x00 }, /* MicroSoft */
791    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* MouseSystems */
792    {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* Logitech */
793    {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* MMSeries */
794    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x23,  0x00 }, /* MouseMan */
795    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* Bus */
796    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* InPort */
797    {	0xc0,	0x00,	0x00,	0x00,	3,    0x00,  0xff }, /* PS/2 mouse */
798    {	0xe0,	0x80,	0x80,	0x00,	3,    0x00,  0xff }, /* MM HitTablet */
799    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* GlidePoint */
800    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x3f,  0x00 }, /* IntelliMouse */
801    { 	0x40,	0x40,	0x40,	0x00,	3,   ~0x33,  0x00 }, /* ThinkingMouse */
802    {	0xf8,	0x80,	0x00,	0x00,	5,    0x00,  0xff }, /* sysmouse */
803#if notyet
804    {	0xf8,	0x80,	0x00,	0x00,	5,   ~0x2f,  0x10 }, /* Mariqua */
805#endif
806};
807static unsigned char cur_proto[7];
808
809static int
810r_identify(void)
811{
812    char pnpbuf[256];	/* PnP identifier string may be up to 256 bytes long */
813    pnpid_t pnpid;
814    symtab_t *t;
815    int level;
816    int len;
817
818    /* set the driver operation level, if applicable */
819    if (rodent.level < 0)
820	rodent.level = 1;
821    ioctl(rodent.mfd, MOUSE_SETLEVEL, &rodent.level);
822    rodent.level = (ioctl(rodent.mfd, MOUSE_GETLEVEL, &level) == 0) ? level : 0;
823
824    /*
825     * Interrogate the driver and get some intelligence on the device...
826     * The following ioctl functions are not always supported by device
827     * drivers.  When the driver doesn't support them, we just trust the
828     * user to supply valid information.
829     */
830    rodent.hw.iftype = MOUSE_IF_UNKNOWN;
831    rodent.hw.model = MOUSE_MODEL_GENERIC;
832    ioctl(rodent.mfd, MOUSE_GETHWINFO, &rodent.hw);
833
834    if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
835        bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
836    rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
837    rodent.mode.rate = -1;
838    rodent.mode.resolution = MOUSE_RES_UNKNOWN;
839    rodent.mode.accelfactor = 0;
840    rodent.mode.level = 0;
841    if (ioctl(rodent.mfd, MOUSE_GETMODE, &rodent.mode) == 0) {
842        if ((rodent.mode.protocol == MOUSE_PROTO_UNKNOWN)
843	    || (rodent.mode.protocol >= sizeof(proto)/sizeof(proto[0]))) {
844	    logwarnx("unknown mouse protocol (%d)", rodent.mode.protocol);
845	    return MOUSE_PROTO_UNKNOWN;
846        } else {
847	    /* INPORT and BUS are the same... */
848	    if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
849	        rodent.mode.protocol = MOUSE_PROTO_BUS;
850	    if (rodent.mode.protocol != rodent.rtype) {
851		/* Hmm, the driver doesn't agree with the user... */
852                if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
853	            logwarnx("mouse type mismatch (%s != %s), %s is assumed",
854		        r_name(rodent.mode.protocol), r_name(rodent.rtype),
855		        r_name(rodent.mode.protocol));
856	        rodent.rtype = rodent.mode.protocol;
857                bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
858	    }
859        }
860        cur_proto[4] = rodent.mode.packetsize;
861        cur_proto[0] = rodent.mode.syncmask[0];	/* header byte bit mask */
862        cur_proto[1] = rodent.mode.syncmask[1];	/* header bit pattern */
863    }
864
865    /* maybe this is an PnP mouse... */
866    if (rodent.mode.protocol == MOUSE_PROTO_UNKNOWN) {
867
868        if (rodent.flags & NoPnP)
869            return rodent.rtype;
870	if (((len = pnpgets(pnpbuf)) <= 0) || !pnpparse(&pnpid, pnpbuf, len))
871            return rodent.rtype;
872
873        debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
874	    pnpid.neisaid, pnpid.neisaid, pnpid.eisaid,
875	    pnpid.ncompat, pnpid.ncompat, pnpid.compat,
876	    pnpid.ndescription, pnpid.ndescription, pnpid.description);
877
878	/* we have a valid PnP serial device ID */
879        rodent.hw.iftype = MOUSE_IF_SERIAL;
880	t = pnpproto(&pnpid);
881	if (t != NULL) {
882            rodent.mode.protocol = t->val;
883            rodent.hw.model = t->val2;
884	} else {
885            rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
886	}
887	if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
888	    rodent.mode.protocol = MOUSE_PROTO_BUS;
889
890        /* make final adjustment */
891	if (rodent.mode.protocol != MOUSE_PROTO_UNKNOWN) {
892	    if (rodent.mode.protocol != rodent.rtype) {
893		/* Hmm, the device doesn't agree with the user... */
894                if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
895	            logwarnx("mouse type mismatch (%s != %s), %s is assumed",
896		        r_name(rodent.mode.protocol), r_name(rodent.rtype),
897		        r_name(rodent.mode.protocol));
898	        rodent.rtype = rodent.mode.protocol;
899                bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
900	    }
901	}
902    }
903
904    debug("proto params: %02x %02x %02x %02x %d %02x %02x",
905	cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
906	cur_proto[4], cur_proto[5], cur_proto[6]);
907
908    return rodent.rtype;
909}
910
911static char *
912r_if(int iftype)
913{
914    char *s;
915
916    s = gettokenname(rifs, iftype);
917    return (s == NULL) ? "unknown" : s;
918}
919
920static char *
921r_name(int type)
922{
923    return ((type == MOUSE_PROTO_UNKNOWN)
924	|| (type > sizeof(rnames)/sizeof(rnames[0]) - 1))
925	? "unknown" : rnames[type];
926}
927
928static char *
929r_model(int model)
930{
931    char *s;
932
933    s = gettokenname(rmodels, model);
934    return (s == NULL) ? "unknown" : s;
935}
936
937static void
938r_init(void)
939{
940    fd_set fds;
941    char *s;
942    char c;
943    int i;
944
945    /**
946     ** This comment is a little out of context here, but it contains
947     ** some useful information...
948     ********************************************************************
949     **
950     ** The following lines take care of the Logitech MouseMan protocols.
951     **
952     ** NOTE: There are different versions of both MouseMan and TrackMan!
953     **       Hence I add another protocol P_LOGIMAN, which the user can
954     **       specify as MouseMan in his XF86Config file. This entry was
955     **       formerly handled as a special case of P_MS. However, people
956     **       who don't have the middle button problem, can still specify
957     **       Microsoft and use P_MS.
958     **
959     ** By default, these mice should use a 3 byte Microsoft protocol
960     ** plus a 4th byte for the middle button. However, the mouse might
961     ** have switched to a different protocol before we use it, so I send
962     ** the proper sequence just in case.
963     **
964     ** NOTE: - all commands to (at least the European) MouseMan have to
965     **         be sent at 1200 Baud.
966     **       - each command starts with a '*'.
967     **       - whenever the MouseMan receives a '*', it will switch back
968     **	 to 1200 Baud. Hence I have to select the desired protocol
969     **	 first, then select the baud rate.
970     **
971     ** The protocols supported by the (European) MouseMan are:
972     **   -  5 byte packed binary protocol, as with the Mouse Systems
973     **      mouse. Selected by sequence "*U".
974     **   -  2 button 3 byte MicroSoft compatible protocol. Selected
975     **      by sequence "*V".
976     **   -  3 button 3+1 byte MicroSoft compatible protocol (default).
977     **      Selected by sequence "*X".
978     **
979     ** The following baud rates are supported:
980     **   -  1200 Baud (default). Selected by sequence "*n".
981     **   -  9600 Baud. Selected by sequence "*q".
982     **
983     ** Selecting a sample rate is no longer supported with the MouseMan!
984     ** Some additional lines in xf86Config.c take care of ill configured
985     ** baud rates and sample rates. (The user will get an error.)
986     */
987
988    switch (rodent.rtype) {
989
990    case MOUSE_PROTO_LOGI:
991	/*
992	 * The baud rate selection command must be sent at the current
993	 * baud rate; try all likely settings
994	 */
995	setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
996	setmousespeed(4800, rodent.baudrate, rodentcflags[rodent.rtype]);
997	setmousespeed(2400, rodent.baudrate, rodentcflags[rodent.rtype]);
998	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
999	/* select MM series data format */
1000	write(rodent.mfd, "S", 1);
1001	setmousespeed(rodent.baudrate, rodent.baudrate,
1002		      rodentcflags[MOUSE_PROTO_MM]);
1003	/* select report rate/frequency */
1004	if      (rodent.rate <= 0)   write(rodent.mfd, "O", 1);
1005	else if (rodent.rate <= 15)  write(rodent.mfd, "J", 1);
1006	else if (rodent.rate <= 27)  write(rodent.mfd, "K", 1);
1007	else if (rodent.rate <= 42)  write(rodent.mfd, "L", 1);
1008	else if (rodent.rate <= 60)  write(rodent.mfd, "R", 1);
1009	else if (rodent.rate <= 85)  write(rodent.mfd, "M", 1);
1010	else if (rodent.rate <= 125) write(rodent.mfd, "Q", 1);
1011	else			     write(rodent.mfd, "N", 1);
1012	break;
1013
1014    case MOUSE_PROTO_LOGIMOUSEMAN:
1015	/* The command must always be sent at 1200 baud */
1016	setmousespeed(1200, 1200, rodentcflags[rodent.rtype]);
1017	write(rodent.mfd, "*X", 2);
1018	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1019	break;
1020
1021    case MOUSE_PROTO_HITTAB:
1022	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1023
1024	/*
1025	 * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
1026	 * The tablet must be configured to be in MM mode, NO parity,
1027	 * Binary Format.  xf86Info.sampleRate controls the sensativity
1028	 * of the tablet.  We only use this tablet for it's 4-button puck
1029	 * so we don't run in "Absolute Mode"
1030	 */
1031	write(rodent.mfd, "z8", 2);	/* Set Parity = "NONE" */
1032	usleep(50000);
1033	write(rodent.mfd, "zb", 2);	/* Set Format = "Binary" */
1034	usleep(50000);
1035	write(rodent.mfd, "@", 1);	/* Set Report Mode = "Stream" */
1036	usleep(50000);
1037	write(rodent.mfd, "R", 1);	/* Set Output Rate = "45 rps" */
1038	usleep(50000);
1039	write(rodent.mfd, "I\x20", 2);	/* Set Incrememtal Mode "20" */
1040	usleep(50000);
1041	write(rodent.mfd, "E", 1);	/* Set Data Type = "Relative */
1042	usleep(50000);
1043
1044	/* Resolution is in 'lines per inch' on the Hitachi tablet */
1045	if      (rodent.resolution == MOUSE_RES_LOW) 		c = 'g';
1046	else if (rodent.resolution == MOUSE_RES_MEDIUMLOW)	c = 'e';
1047	else if (rodent.resolution == MOUSE_RES_MEDIUMHIGH)	c = 'h';
1048	else if (rodent.resolution == MOUSE_RES_HIGH)		c = 'd';
1049	else if (rodent.resolution <=   40) 			c = 'g';
1050	else if (rodent.resolution <=  100) 			c = 'd';
1051	else if (rodent.resolution <=  200) 			c = 'e';
1052	else if (rodent.resolution <=  500) 			c = 'h';
1053	else if (rodent.resolution <= 1000) 			c = 'j';
1054	else                                			c = 'd';
1055	write(rodent.mfd, &c, 1);
1056	usleep(50000);
1057
1058	write(rodent.mfd, "\021", 1);	/* Resume DATA output */
1059	break;
1060
1061    case MOUSE_PROTO_THINK:
1062	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1063	/* the PnP ID string may be sent again, discard it */
1064	usleep(200000);
1065	i = FREAD;
1066	ioctl(rodent.mfd, TIOCFLUSH, &i);
1067	/* send the command to initialize the beast */
1068	for (s = "E5E5"; *s; ++s) {
1069	    write(rodent.mfd, s, 1);
1070	    FD_ZERO(&fds);
1071	    FD_SET(rodent.mfd, &fds);
1072	    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1073		break;
1074	    read(rodent.mfd, &c, 1);
1075	    debug("%c", c);
1076	    if (c != *s)
1077	        break;
1078	}
1079	break;
1080
1081    case MOUSE_PROTO_MSC:
1082	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1083	if (rodent.flags & ClearDTR) {
1084	   i = TIOCM_DTR;
1085	   ioctl(rodent.mfd, TIOCMBIC, &i);
1086        }
1087        if (rodent.flags & ClearRTS) {
1088	   i = TIOCM_RTS;
1089	   ioctl(rodent.mfd, TIOCMBIC, &i);
1090        }
1091	break;
1092
1093    case MOUSE_PROTO_SYSMOUSE:
1094	if (rodent.hw.iftype == MOUSE_IF_SYSMOUSE)
1095	    setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1096	/* fall through */
1097
1098    case MOUSE_PROTO_BUS:
1099    case MOUSE_PROTO_INPORT:
1100    case MOUSE_PROTO_PS2:
1101	if (rodent.rate >= 0)
1102	    rodent.mode.rate = rodent.rate;
1103	if (rodent.resolution != MOUSE_RES_UNKNOWN)
1104	    rodent.mode.resolution = rodent.resolution;
1105	ioctl(rodent.mfd, MOUSE_SETMODE, &rodent.mode);
1106	break;
1107
1108    default:
1109	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1110	break;
1111    }
1112}
1113
1114static int
1115r_protocol(u_char rBuf, mousestatus_t *act)
1116{
1117    /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1118    static int butmapmss[4] = {	/* Microsoft, MouseMan, GlidePoint,
1119				   IntelliMouse, Thinking Mouse */
1120	0,
1121	MOUSE_BUTTON3DOWN,
1122	MOUSE_BUTTON1DOWN,
1123	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1124    };
1125    static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
1126				    Thinking Mouse */
1127	0,
1128	MOUSE_BUTTON4DOWN,
1129	MOUSE_BUTTON2DOWN,
1130	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1131    };
1132    /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1133    static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
1134				       MouseMan+ */
1135	0,
1136	MOUSE_BUTTON2DOWN,
1137	MOUSE_BUTTON4DOWN,
1138	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1139    };
1140    /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
1141    static int butmapmsc[8] = {	/* MouseSystems, MMSeries, Logitech,
1142				   Bus, sysmouse */
1143	0,
1144	MOUSE_BUTTON3DOWN,
1145	MOUSE_BUTTON2DOWN,
1146	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1147	MOUSE_BUTTON1DOWN,
1148	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1149	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1150	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1151    };
1152    /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1153    static int butmapps2[8] = {	/* PS/2 */
1154	0,
1155	MOUSE_BUTTON1DOWN,
1156	MOUSE_BUTTON3DOWN,
1157	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1158	MOUSE_BUTTON2DOWN,
1159	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1160	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1161	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1162    };
1163    /* for Hitachi tablet */
1164    static int butmaphit[8] = {	/* MM HitTablet */
1165	0,
1166	MOUSE_BUTTON3DOWN,
1167	MOUSE_BUTTON2DOWN,
1168	MOUSE_BUTTON1DOWN,
1169	MOUSE_BUTTON4DOWN,
1170	MOUSE_BUTTON5DOWN,
1171	MOUSE_BUTTON6DOWN,
1172	MOUSE_BUTTON7DOWN,
1173    };
1174    static int           pBufP = 0;
1175    static unsigned char pBuf[8];
1176
1177    debug("received char 0x%x",(int)rBuf);
1178
1179    /*
1180     * Hack for resyncing: We check here for a package that is:
1181     *  a) illegal (detected by wrong data-package header)
1182     *  b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
1183     *  c) bad header-package
1184     *
1185     * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
1186     *       -128 are allowed, but since they are very seldom we can easily
1187     *       use them as package-header with no button pressed.
1188     * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
1189     *         0x80 is not valid as a header byte. For a PS/2 mouse we skip
1190     *         checking data bytes.
1191     *         For resyncing a PS/2 mouse we require the two most significant
1192     *         bits in the header byte to be 0. These are the overflow bits,
1193     *         and in case of an overflow we actually lose sync. Overflows
1194     *         are very rare, however, and we quickly gain sync again after
1195     *         an overflow condition. This is the best we can do. (Actually,
1196     *         we could use bit 0x08 in the header byte for resyncing, since
1197     *         that bit is supposed to be always on, but nobody told
1198     *         Microsoft...)
1199     */
1200
1201    if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
1202	((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
1203    {
1204	pBufP = 0;		/* skip package */
1205    }
1206
1207    if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
1208	return 0;
1209
1210    /* is there an extra data byte? */
1211    if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
1212    {
1213	/*
1214	 * Hack for Logitech MouseMan Mouse - Middle button
1215	 *
1216	 * Unfortunately this mouse has variable length packets: the standard
1217	 * Microsoft 3 byte packet plus an optional 4th byte whenever the
1218	 * middle button status changes.
1219	 *
1220	 * We have already processed the standard packet with the movement
1221	 * and button info.  Now post an event message with the old status
1222	 * of the left and right buttons and the updated middle button.
1223	 */
1224
1225	/*
1226	 * Even worse, different MouseMen and TrackMen differ in the 4th
1227	 * byte: some will send 0x00/0x20, others 0x01/0x21, or even
1228	 * 0x02/0x22, so I have to strip off the lower bits.
1229         *
1230         * [JCH-96/01/21]
1231         * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
1232         * and it is activated by tapping the glidepad with the finger! 8^)
1233         * We map it to bit bit3, and the reverse map in xf86Events just has
1234         * to be extended so that it is identified as Button 4. The lower
1235         * half of the reverse-map may remain unchanged.
1236	 */
1237
1238        /*
1239	 * [KY-97/08/03]
1240	 * Receive the fourth byte only when preceeding three bytes have
1241	 * been detected (pBufP >= cur_proto[4]).  In the previous
1242	 * versions, the test was pBufP == 0; thus, we may have mistakingly
1243	 * received a byte even if we didn't see anything preceeding
1244	 * the byte.
1245	 */
1246
1247	if ((rBuf & cur_proto[5]) != cur_proto[6]) {
1248            pBufP = 0;
1249	    return 0;
1250	}
1251
1252	switch (rodent.rtype) {
1253#if notyet
1254	case MOUSE_PROTO_MARIQUA:
1255	    /*
1256	     * This mouse has 16! buttons in addition to the standard
1257	     * three of them.  They return 0x10 though 0x1f in the
1258	     * so-called `ten key' mode and 0x30 though 0x3f in the
1259	     * `function key' mode.  As there are only 31 bits for
1260	     * button state (including the standard three), we ignore
1261	     * the bit 0x20 and don't distinguish the two modes.
1262	     */
1263	    act->dx = act->dy = act->dz = 0;
1264	    act->obutton = act->button;
1265	    rBuf &= 0x1f;
1266	    act->button = (1 << (rBuf - 13))
1267                | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1268	    /*
1269	     * FIXME: this is a button "down" event. There needs to be
1270	     * a corresponding button "up" event... XXX
1271	     */
1272	    break;
1273#endif /* notyet */
1274
1275	/*
1276	 * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
1277	 * always send the fourth byte, whereas the fourth byte is
1278	 * optional for GlidePoint and ThinkingMouse. The fourth byte
1279	 * is also optional for MouseMan+ and FirstMouse+ in their
1280	 * native mode. It is always sent if they are in the IntelliMouse
1281	 * compatible mode.
1282	 */
1283	case MOUSE_PROTO_INTELLI:	/* IntelliMouse, NetMouse, Mie Mouse,
1284					   MouseMan+ */
1285	    act->dx = act->dy = 0;
1286	    act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
1287	    act->obutton = act->button;
1288	    act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1289		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1290	    break;
1291
1292	default:
1293	    act->dx = act->dy = act->dz = 0;
1294	    act->obutton = act->button;
1295	    act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1296		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1297	    break;
1298	}
1299
1300	act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1301	    | (act->obutton ^ act->button);
1302        pBufP = 0;
1303	return act->flags;
1304    }
1305
1306    if (pBufP >= cur_proto[4])
1307	pBufP = 0;
1308    pBuf[pBufP++] = rBuf;
1309    if (pBufP != cur_proto[4])
1310	return 0;
1311
1312    /*
1313     * assembly full package
1314     */
1315
1316    debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
1317	cur_proto[4],
1318	pBuf[0], pBuf[1], pBuf[2], pBuf[3],
1319	pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
1320
1321    act->dz = 0;
1322    act->obutton = act->button;
1323    switch (rodent.rtype)
1324    {
1325    case MOUSE_PROTO_MS:		/* Microsoft */
1326    case MOUSE_PROTO_LOGIMOUSEMAN:	/* MouseMan/TrackMan */
1327	if (rodent.flags & ChordMiddle)
1328	    act->button = ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
1329		? MOUSE_BUTTON2DOWN
1330		: butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1331	else
1332	    act->button = (act->obutton & MOUSE_BUTTON2DOWN)
1333		| butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1334	act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1335	act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1336	break;
1337
1338    case MOUSE_PROTO_GLIDEPOINT:	/* GlidePoint */
1339    case MOUSE_PROTO_THINK:		/* ThinkingMouse */
1340    case MOUSE_PROTO_INTELLI:		/* IntelliMouse, NetMouse, Mie Mouse,
1341					   MouseMan+ */
1342	act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
1343            | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1344	act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1345	act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1346	break;
1347
1348    case MOUSE_PROTO_MSC:		/* MouseSystems Corp */
1349#if notyet
1350    case MOUSE_PROTO_MARIQUA:		/* Mariqua */
1351#endif
1352	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1353	act->dx =    (char)(pBuf[1]) + (char)(pBuf[3]);
1354	act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
1355	break;
1356
1357    case MOUSE_PROTO_HITTAB:		/* MM HitTablet */
1358	act->button = butmaphit[pBuf[0] & 0x07];
1359	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1360	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1361	break;
1362
1363    case MOUSE_PROTO_MM:		/* MM Series */
1364    case MOUSE_PROTO_LOGI:		/* Logitech Mice */
1365	act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
1366	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1367	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1368	break;
1369
1370    case MOUSE_PROTO_BUS:		/* Bus */
1371    case MOUSE_PROTO_INPORT:		/* InPort */
1372	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1373	act->dx =   (char)pBuf[1];
1374	act->dy = - (char)pBuf[2];
1375	break;
1376
1377    case MOUSE_PROTO_PS2:		/* PS/2 */
1378	act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
1379	act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ?    pBuf[1] - 256  :  pBuf[1];
1380	act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ?  -(pBuf[2] - 256) : -pBuf[2];
1381	/*
1382	 * Moused usually operates the psm driver at the operation level 1
1383	 * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
1384	 * The following code takes effect only when the user explicitly
1385	 * requets the level 2 at which wheel movement and additional button
1386	 * actions are encoded in model-dependent formats. At the level 0
1387	 * the following code is no-op because the psm driver says the model
1388	 * is MOUSE_MODEL_GENERIC.
1389	 */
1390	switch (rodent.hw.model) {
1391	case MOUSE_MODEL_INTELLI:
1392	case MOUSE_MODEL_NET:
1393	    /* wheel data is in the fourth byte */
1394	    act->dz = (char)pBuf[3];
1395	    break;
1396	case MOUSE_MODEL_MOUSEMANPLUS:
1397	    if ((pBuf[0] & ~MOUSE_PS2_BUTTONS) == 0xc8) {
1398		/* the extended data packet encodes button and wheel events */
1399		act->dx = act->dy = 0;
1400		act->dz = (pBuf[1] & MOUSE_PS2PLUS_ZNEG)
1401		    ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
1402		act->button |= ((pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
1403		    ? MOUSE_BUTTON4DOWN : 0);
1404	    } else {
1405		/* preserve button states */
1406		act->button |= act->obutton & MOUSE_EXTBUTTONS;
1407	    }
1408	    break;
1409	case MOUSE_MODEL_GLIDEPOINT:
1410	    /* `tapping' action */
1411	    act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
1412	    break;
1413	case MOUSE_MODEL_NETSCROLL:
1414	    /* three addtional bytes encode button and wheel events */
1415	    act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
1416		? MOUSE_BUTTON4DOWN : 0;
1417	    act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
1418	    break;
1419	case MOUSE_MODEL_THINK:
1420	    /* the fourth button state in the first byte */
1421	    act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
1422	    break;
1423	case MOUSE_MODEL_GENERIC:
1424	default:
1425	    break;
1426	}
1427	break;
1428
1429    case MOUSE_PROTO_SYSMOUSE:		/* sysmouse */
1430	act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
1431	act->dx =    (char)(pBuf[1]) + (char)(pBuf[3]);
1432	act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
1433	if (rodent.level == 1) {
1434	    act->dz = ((char)(pBuf[5] << 1) + (char)(pBuf[6] << 1))/2;
1435	    act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
1436	}
1437	break;
1438
1439    default:
1440	return 0;
1441    }
1442    /*
1443     * We don't reset pBufP here yet, as there may be an additional data
1444     * byte in some protocols. See above.
1445     */
1446
1447    /* has something changed? */
1448    act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1449	| (act->obutton ^ act->button);
1450
1451    if (rodent.flags & Emulate3Button) {
1452	if (((act->flags & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1453	        == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1454	    && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1455	        == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) {
1456	    act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN);
1457	    act->button |= MOUSE_BUTTON2DOWN;
1458	} else if ((act->obutton & MOUSE_BUTTON2DOWN)
1459	    && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1460	        != (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) {
1461	    act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN
1462			       | MOUSE_BUTTON3DOWN);
1463	}
1464	act->flags &= MOUSE_POSCHANGED;
1465	act->flags |= act->obutton ^ act->button;
1466    }
1467
1468    return act->flags;
1469}
1470
1471/* phisical to logical button mapping */
1472static int p2l[MOUSE_MAXBUTTON] = {
1473    MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
1474    MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
1475    0x00000100,        0x00000200,        0x00000400,        0x00000800,
1476    0x00001000,        0x00002000,        0x00004000,        0x00008000,
1477    0x00010000,        0x00020000,        0x00040000,        0x00080000,
1478    0x00100000,        0x00200000,        0x00400000,        0x00800000,
1479    0x01000000,        0x02000000,        0x04000000,        0x08000000,
1480    0x10000000,        0x20000000,        0x40000000,
1481};
1482
1483static char *
1484skipspace(char *s)
1485{
1486    while(isspace(*s))
1487	++s;
1488    return s;
1489}
1490
1491static int
1492r_installmap(char *arg)
1493{
1494    int pbutton;
1495    int lbutton;
1496    char *s;
1497
1498    while (*arg) {
1499	arg = skipspace(arg);
1500	s = arg;
1501	while (isdigit(*arg))
1502	    ++arg;
1503	arg = skipspace(arg);
1504	if ((arg <= s) || (*arg != '='))
1505	    return FALSE;
1506	lbutton = atoi(s);
1507
1508	arg = skipspace(++arg);
1509	s = arg;
1510	while (isdigit(*arg))
1511	    ++arg;
1512	if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
1513	    return FALSE;
1514	pbutton = atoi(s);
1515
1516	if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
1517	    return FALSE;
1518	if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
1519	    return FALSE;
1520	p2l[pbutton - 1] = 1 << (lbutton - 1);
1521    }
1522
1523    return TRUE;
1524}
1525
1526static void
1527r_map(mousestatus_t *act1, mousestatus_t *act2)
1528{
1529    register int pb;
1530    register int pbuttons;
1531    int lbuttons;
1532
1533    pbuttons = act1->button;
1534    lbuttons = 0;
1535
1536    act2->obutton = act2->button;
1537    act2->dx = act1->dx;
1538    act2->dy = act1->dy;
1539    act2->dz = act1->dz;
1540
1541    switch (rodent.zmap) {
1542    case 0:	/* do nothing */
1543	break;
1544    case MOUSE_XAXIS:
1545	if (act1->dz != 0) {
1546	    act2->dx = act1->dz;
1547	    act2->dz = 0;
1548	}
1549	break;
1550    case MOUSE_YAXIS:
1551	if (act1->dz != 0) {
1552	    act2->dy = act1->dz;
1553	    act2->dz = 0;
1554	}
1555	break;
1556    default:	/* buttons */
1557	pbuttons &= ~(rodent.zmap | (rodent.zmap << 1));
1558	if (act1->dz < 0)
1559	    pbuttons |= rodent.zmap;
1560	else if (act1->dz > 0)
1561	    pbuttons |= (rodent.zmap << 1);
1562	act2->dz = 0;
1563	break;
1564    }
1565
1566    for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
1567	lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
1568	pbuttons >>= 1;
1569    }
1570    act2->button = lbuttons;
1571
1572    act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
1573	| (act2->obutton ^ act2->button);
1574}
1575
1576static void
1577r_click(mousestatus_t *act)
1578{
1579    struct mouse_info mouse;
1580    struct timeval tv;
1581    struct timeval tv1;
1582    struct timeval tv2;
1583    struct timezone tz;
1584    int button;
1585    int mask;
1586    int i;
1587
1588    mask = act->flags & MOUSE_BUTTONS;
1589    if (mask == 0)
1590	return;
1591
1592    gettimeofday(&tv1, &tz);
1593    tv2.tv_sec = rodent.clickthreshold/1000;
1594    tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
1595    timersub(&tv1, &tv2, &tv);
1596    debug("tv:  %ld %ld", tv.tv_sec, tv.tv_usec);
1597    button = MOUSE_BUTTON1DOWN;
1598    for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
1599        if (mask & 1) {
1600            if (act->button & button) {
1601                /* the button is down */
1602    		debug("  :  %ld %ld",
1603		    buttonstate[i].tv.tv_sec, buttonstate[i].tv.tv_usec);
1604		if (timercmp(&tv, &buttonstate[i].tv, >)) {
1605                    buttonstate[i].tv.tv_sec = 0;
1606                    buttonstate[i].tv.tv_usec = 0;
1607                    buttonstate[i].count = 1;
1608                } else {
1609                    ++buttonstate[i].count;
1610                }
1611	        mouse.u.event.value = buttonstate[i].count;
1612            } else {
1613                /* the button is up */
1614                buttonstate[i].tv = tv1;
1615	        mouse.u.event.value = 0;
1616            }
1617	    mouse.operation = MOUSE_BUTTON_EVENT;
1618	    mouse.u.event.id = button;
1619	    if (debug < 2)
1620	        ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
1621	    debug("button %d  count %d", i + 1, mouse.u.event.value);
1622        }
1623	button <<= 1;
1624	mask >>= 1;
1625    }
1626}
1627
1628/* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
1629/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
1630/*
1631 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
1632 *
1633 * Permission to use, copy, modify, distribute, and sell this software and its
1634 * documentation for any purpose is hereby granted without fee, provided that
1635 * the above copyright notice appear in all copies and that both that
1636 * copyright notice and this permission notice appear in supporting
1637 * documentation, and that the name of David Dawes
1638 * not be used in advertising or publicity pertaining to distribution of
1639 * the software without specific, written prior permission.
1640 * David Dawes makes no representations about the suitability of this
1641 * software for any purpose.  It is provided "as is" without express or
1642 * implied warranty.
1643 *
1644 * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
1645 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
1646 * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
1647 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
1648 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
1649 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1650 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1651 *
1652 */
1653
1654
1655static void
1656setmousespeed(int old, int new, unsigned cflag)
1657{
1658	struct termios tty;
1659	char *c;
1660
1661	if (tcgetattr(rodent.mfd, &tty) < 0)
1662	{
1663		logwarn("unable to get status of mouse fd", 0);
1664		return;
1665	}
1666
1667	tty.c_iflag = IGNBRK | IGNPAR;
1668	tty.c_oflag = 0;
1669	tty.c_lflag = 0;
1670	tty.c_cflag = (tcflag_t)cflag;
1671	tty.c_cc[VTIME] = 0;
1672	tty.c_cc[VMIN] = 1;
1673
1674	switch (old)
1675	{
1676	case 9600:
1677		cfsetispeed(&tty, B9600);
1678		cfsetospeed(&tty, B9600);
1679		break;
1680	case 4800:
1681		cfsetispeed(&tty, B4800);
1682		cfsetospeed(&tty, B4800);
1683		break;
1684	case 2400:
1685		cfsetispeed(&tty, B2400);
1686		cfsetospeed(&tty, B2400);
1687		break;
1688	case 1200:
1689	default:
1690		cfsetispeed(&tty, B1200);
1691		cfsetospeed(&tty, B1200);
1692	}
1693
1694	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
1695	{
1696		logwarn("unable to set status of mouse fd", 0);
1697		return;
1698	}
1699
1700	switch (new)
1701	{
1702	case 9600:
1703		c = "*q";
1704		cfsetispeed(&tty, B9600);
1705		cfsetospeed(&tty, B9600);
1706		break;
1707	case 4800:
1708		c = "*p";
1709		cfsetispeed(&tty, B4800);
1710		cfsetospeed(&tty, B4800);
1711		break;
1712	case 2400:
1713		c = "*o";
1714		cfsetispeed(&tty, B2400);
1715		cfsetospeed(&tty, B2400);
1716		break;
1717	case 1200:
1718	default:
1719		c = "*n";
1720		cfsetispeed(&tty, B1200);
1721		cfsetospeed(&tty, B1200);
1722	}
1723
1724	if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
1725	    || rodent.rtype == MOUSE_PROTO_LOGI)
1726	{
1727		if (write(rodent.mfd, c, 2) != 2)
1728		{
1729			logwarn("unable to write to mouse fd", 0);
1730			return;
1731		}
1732	}
1733	usleep(100000);
1734
1735	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
1736		logwarn("unable to set status of mouse fd", 0);
1737}
1738
1739/*
1740 * PnP COM device support
1741 *
1742 * It's a simplistic implementation, but it works :-)
1743 * KY, 31/7/97.
1744 */
1745
1746/*
1747 * Try to elicit a PnP ID as described in
1748 * Microsoft, Hayes: "Plug and Play External COM Device Specification,
1749 * rev 1.00", 1995.
1750 *
1751 * The routine does not fully implement the COM Enumerator as par Section
1752 * 2.1 of the document.  In particular, we don't have idle state in which
1753 * the driver software monitors the com port for dynamic connection or
1754 * removal of a device at the port, because `moused' simply quits if no
1755 * device is found.
1756 *
1757 * In addition, as PnP COM device enumeration procedure slightly has
1758 * changed since its first publication, devices which follow earlier
1759 * revisions of the above spec. may fail to respond if the rev 1.0
1760 * procedure is used. XXX
1761 */
1762static int
1763pnpgets(char *buf)
1764{
1765    struct timeval timeout;
1766    fd_set fds;
1767    int i;
1768    char c;
1769
1770#if 0
1771    /*
1772     * This is the procedure described in rev 1.0 of PnP COM device spec.
1773     * Unfortunately, some devices which comform to earlier revisions of
1774     * the spec gets confused and do not return the ID string...
1775     */
1776
1777    /* port initialization (2.1.2) */
1778    ioctl(rodent.mfd, TIOCMGET, &i);
1779    i |= TIOCM_DTR;		/* DTR = 1 */
1780    i &= ~TIOCM_RTS;		/* RTS = 0 */
1781    ioctl(rodent.mfd, TIOCMSET, &i);
1782    usleep(200000);
1783    if ((ioctl(rodent.mfd, TIOCMGET, &i) == -1) || ((i & TIOCM_DSR) == 0))
1784	goto disconnect_idle;
1785
1786    /* port setup, 1st phase (2.1.3) */
1787    setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
1788    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
1789    ioctl(rodent.mfd, TIOCMBIC, &i);
1790    usleep(200000);
1791    i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
1792    ioctl(rodent.mfd, TIOCMBIS, &i);
1793    usleep(200000);
1794
1795    /* wait for response, 1st phase (2.1.4) */
1796    i = FREAD;
1797    ioctl(rodent.mfd, TIOCFLUSH, &i);
1798    i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
1799    ioctl(rodent.mfd, TIOCMBIS, &i);
1800
1801    /* try to read something */
1802    FD_ZERO(&fds);
1803    FD_SET(rodent.mfd, &fds);
1804    timeout.tv_sec = 0;
1805    timeout.tv_usec = 200000;
1806    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0) {
1807
1808	/* port setup, 2nd phase (2.1.5) */
1809        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
1810        ioctl(rodent.mfd, TIOCMBIC, &i);
1811        usleep(200000);
1812
1813	/* wait for respose, 2nd phase (2.1.6) */
1814        i = FREAD;
1815        ioctl(rodent.mfd, TIOCFLUSH, &i);
1816        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
1817        ioctl(rodent.mfd, TIOCMBIS, &i);
1818
1819        /* try to read something */
1820        FD_ZERO(&fds);
1821        FD_SET(rodent.mfd, &fds);
1822        timeout.tv_sec = 0;
1823        timeout.tv_usec = 200000;
1824        if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
1825	    goto connect_idle;
1826    }
1827#else
1828    /*
1829     * This is a simplified procedure; it simply toggles RTS.
1830     */
1831
1832    ioctl(rodent.mfd, TIOCMGET, &i);
1833    i |= TIOCM_DTR;		/* DTR = 1 */
1834    i &= ~TIOCM_RTS;		/* RTS = 0 */
1835    ioctl(rodent.mfd, TIOCMSET, &i);
1836    usleep(200000);
1837
1838    setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
1839
1840    /* wait for respose */
1841    i = FREAD;
1842    ioctl(rodent.mfd, TIOCFLUSH, &i);
1843    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
1844    ioctl(rodent.mfd, TIOCMBIS, &i);
1845
1846    /* try to read something */
1847    FD_ZERO(&fds);
1848    FD_SET(rodent.mfd, &fds);
1849    timeout.tv_sec = 0;
1850    timeout.tv_usec = 200000;
1851    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
1852        goto connect_idle;
1853#endif
1854
1855    /* collect PnP COM device ID (2.1.7) */
1856    i = 0;
1857    usleep(200000);	/* the mouse must send `Begin ID' within 200msec */
1858    while (read(rodent.mfd, &c, 1) == 1) {
1859	/* we may see "M", or "M3..." before `Begin ID' */
1860        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
1861	    buf[i++] = c;
1862	    break;
1863        }
1864        debug("%c %02x", c, c);
1865    }
1866    if (i <= 0) {
1867	/* we haven't seen `Begin ID' in time... */
1868	goto connect_idle;
1869    }
1870
1871    ++c;			/* make it `End ID' */
1872    for (;;) {
1873        FD_ZERO(&fds);
1874        FD_SET(rodent.mfd, &fds);
1875        timeout.tv_sec = 0;
1876        timeout.tv_usec = 200000;
1877        if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
1878	    break;
1879
1880	read(rodent.mfd, &buf[i], 1);
1881        if (buf[i++] == c)	/* End ID */
1882	    break;
1883	if (i >= 256)
1884	    break;
1885    }
1886    /* string may not be human readable... */
1887    debug("'%-*.*s'", i, i, buf);
1888    if (buf[i - 1] != c)
1889	goto connect_idle;
1890    return i;
1891
1892    /*
1893     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
1894     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
1895     * assuming there is something at the port even if it didn't
1896     * respond to the PnP enumeration procedure.
1897     */
1898disconnect_idle:
1899    i = TIOCM_DTR | TIOCM_RTS;		/* DTR = 1, RTS = 1 */
1900    ioctl(rodent.mfd, TIOCMBIS, &i);
1901connect_idle:
1902    return 0;
1903}
1904
1905static int
1906pnpparse(pnpid_t *id, char *buf, int len)
1907{
1908    char s[3];
1909    int offset;
1910    int sum = 0;
1911    int i, j;
1912
1913    id->revision = 0;
1914    id->eisaid = NULL;
1915    id->serial = NULL;
1916    id->class = NULL;
1917    id->compat = NULL;
1918    id->description = NULL;
1919    id->neisaid = 0;
1920    id->nserial = 0;
1921    id->nclass = 0;
1922    id->ncompat = 0;
1923    id->ndescription = 0;
1924
1925    offset = 0x28 - buf[0];
1926
1927    /* calculate checksum */
1928    for (i = 0; i < len - 3; ++i) {
1929	sum += buf[i];
1930	buf[i] += offset;
1931    }
1932    sum += buf[len - 1];
1933    for (; i < len; ++i)
1934	buf[i] += offset;
1935    debug("PnP ID string: '%*.*s'", len, len, buf);
1936
1937    /* revision */
1938    buf[1] -= offset;
1939    buf[2] -= offset;
1940    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
1941    debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
1942
1943    /* EISA vender and product ID */
1944    id->eisaid = &buf[3];
1945    id->neisaid = 7;
1946
1947    /* option strings */
1948    i = 10;
1949    if (buf[i] == '\\') {
1950        /* device serial # */
1951        for (j = ++i; i < len; ++i) {
1952            if (buf[i] == '\\')
1953		break;
1954        }
1955	if (i >= len)
1956	    i -= 3;
1957	if (i - j == 8) {
1958            id->serial = &buf[j];
1959            id->nserial = 8;
1960	}
1961    }
1962    if (buf[i] == '\\') {
1963        /* PnP class */
1964        for (j = ++i; i < len; ++i) {
1965            if (buf[i] == '\\')
1966		break;
1967        }
1968	if (i >= len)
1969	    i -= 3;
1970	if (i > j + 1) {
1971            id->class = &buf[j];
1972            id->nclass = i - j;
1973        }
1974    }
1975    if (buf[i] == '\\') {
1976	/* compatible driver */
1977        for (j = ++i; i < len; ++i) {
1978            if (buf[i] == '\\')
1979		break;
1980        }
1981	/*
1982	 * PnP COM spec prior to v0.96 allowed '*' in this field,
1983	 * it's not allowed now; just igore it.
1984	 */
1985	if (buf[j] == '*')
1986	    ++j;
1987	if (i >= len)
1988	    i -= 3;
1989	if (i > j + 1) {
1990            id->compat = &buf[j];
1991            id->ncompat = i - j;
1992        }
1993    }
1994    if (buf[i] == '\\') {
1995	/* product description */
1996        for (j = ++i; i < len; ++i) {
1997            if (buf[i] == ';')
1998		break;
1999        }
2000	if (i >= len)
2001	    i -= 3;
2002	if (i > j + 1) {
2003            id->description = &buf[j];
2004            id->ndescription = i - j;
2005        }
2006    }
2007
2008    /* checksum exists if there are any optional fields */
2009    if ((id->nserial > 0) || (id->nclass > 0)
2010	|| (id->ncompat > 0) || (id->ndescription > 0)) {
2011        debug("PnP checksum: 0x%X", sum);
2012        sprintf(s, "%02X", sum & 0x0ff);
2013        if (strncmp(s, &buf[len - 3], 2) != 0) {
2014#if 0
2015            /*
2016	     * I found some mice do not comply with the PnP COM device
2017	     * spec regarding checksum... XXX
2018	     */
2019            logwarnx("PnP checksum error", 0);
2020	    return FALSE;
2021#endif
2022        }
2023    }
2024
2025    return TRUE;
2026}
2027
2028static symtab_t *
2029pnpproto(pnpid_t *id)
2030{
2031    symtab_t *t;
2032    int i, j;
2033
2034    if (id->nclass > 0)
2035	if (strncmp(id->class, "MOUSE", id->nclass) != 0)
2036	    /* this is not a mouse! */
2037	    return NULL;
2038
2039    if (id->neisaid > 0) {
2040        t = gettoken(pnpprod, id->eisaid, id->neisaid);
2041	if (t->val != MOUSE_PROTO_UNKNOWN)
2042            return t;
2043    }
2044
2045    /*
2046     * The 'Compatible drivers' field may contain more than one
2047     * ID separated by ','.
2048     */
2049    if (id->ncompat <= 0)
2050	return NULL;
2051    for (i = 0; i < id->ncompat; ++i) {
2052        for (j = i; id->compat[i] != ','; ++i)
2053            if (i >= id->ncompat)
2054		break;
2055        if (i > j) {
2056            t = gettoken(pnpprod, id->compat + j, i - j);
2057	    if (t->val != MOUSE_PROTO_UNKNOWN)
2058                return t;
2059	}
2060    }
2061
2062    return NULL;
2063}
2064
2065/* name/val mapping */
2066
2067static symtab_t *
2068gettoken(symtab_t *tab, char *s, int len)
2069{
2070    int i;
2071
2072    for (i = 0; tab[i].name != NULL; ++i) {
2073	if (strncmp(tab[i].name, s, len) == 0)
2074	    break;
2075    }
2076    return &tab[i];
2077}
2078
2079static char *
2080gettokenname(symtab_t *tab, int val)
2081{
2082    int i;
2083
2084    for (i = 0; tab[i].name != NULL; ++i) {
2085	if (tab[i].val == val)
2086	    return tab[i].name;
2087    }
2088    return NULL;
2089}
2090