moused.c revision 31604
1/**
2 ** Copyright (c) 1995 Michael Smith, All rights reserved.
3 **
4 ** Redistribution and use in source and binary forms, with or without
5 ** modification, are permitted provided that the following conditions
6 ** are met:
7 ** 1. Redistributions of source code must retain the above copyright
8 **    notice, this list of conditions and the following disclaimer as
9 **    the first lines of this file unmodified.
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 **    notice, this list of conditions and the following disclaimer in the
12 **    documentation and/or other materials provided with the distribution.
13 ** 3. All advertising materials mentioning features or use of this software
14 **    must display the following acknowledgment:
15 **      This product includes software developed by Michael Smith.
16 ** 4. The name of the author may not be used to endorse or promote products
17 **    derived from this software without specific prior written permission.
18 **
19 **
20 ** THIS SOFTWARE IS PROVIDED BY Michael Smith ``AS IS'' AND ANY
21 ** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith BE LIABLE FOR
24 ** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29 ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 ** EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **
32 **/
33
34/**
35 ** MOUSED.C
36 **
37 ** Mouse daemon : listens to a serial port, the bus mouse interface, or
38 ** the PS/2 mouse port for mouse data stream, interprets data and passes
39 ** ioctls off to the console driver.
40 **
41 ** The mouse interface functions are derived closely from the mouse
42 ** handler in the XFree86 X server.  Many thanks to the XFree86 people
43 ** for their great work!
44 **
45 **/
46
47#ifndef lint
48static const char rcsid[] =
49	"$Id: moused.c,v 1.10 1997/09/25 06:44:39 charnier 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    0,							/* 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_BUS:
1094    case MOUSE_PROTO_INPORT:
1095    case MOUSE_PROTO_PS2:
1096    case MOUSE_PROTO_SYSMOUSE:
1097	if (rodent.rate >= 0)
1098	    rodent.mode.rate = rodent.rate;
1099	if (rodent.resolution != MOUSE_RES_UNKNOWN)
1100	    rodent.mode.resolution = rodent.resolution;
1101	ioctl(rodent.mfd, MOUSE_SETMODE, &rodent.mode);
1102	break;
1103
1104    default:
1105	setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1106	break;
1107    }
1108}
1109
1110static int
1111r_protocol(u_char rBuf, mousestatus_t *act)
1112{
1113    /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1114    static int butmapmss[4] = {	/* Microsoft, MouseMan, GlidePoint,
1115				   IntelliMouse, Thinking Mouse */
1116	0,
1117	MOUSE_BUTTON3DOWN,
1118	MOUSE_BUTTON1DOWN,
1119	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1120    };
1121    static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
1122				    Thinking Mouse */
1123	0,
1124	MOUSE_BUTTON4DOWN,
1125	MOUSE_BUTTON2DOWN,
1126	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1127    };
1128    /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1129    static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
1130				       MouseMan+ */
1131	0,
1132	MOUSE_BUTTON2DOWN,
1133	MOUSE_BUTTON4DOWN,
1134	MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
1135    };
1136    /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
1137    static int butmapmsc[8] = {	/* MouseSystems, MMSeries, Logitech,
1138				   Bus, sysmouse */
1139	0,
1140	MOUSE_BUTTON3DOWN,
1141	MOUSE_BUTTON2DOWN,
1142	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1143	MOUSE_BUTTON1DOWN,
1144	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1145	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1146	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1147    };
1148    /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1149    static int butmapps2[8] = {	/* PS/2 */
1150	0,
1151	MOUSE_BUTTON1DOWN,
1152	MOUSE_BUTTON3DOWN,
1153	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1154	MOUSE_BUTTON2DOWN,
1155	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1156	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1157	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1158    };
1159    /* for Hitachi tablet */
1160    static int butmaphit[8] = {	/* MM HitTablet */
1161	0,
1162	MOUSE_BUTTON3DOWN,
1163	MOUSE_BUTTON2DOWN,
1164	MOUSE_BUTTON1DOWN,
1165	MOUSE_BUTTON4DOWN,
1166	MOUSE_BUTTON5DOWN,
1167	MOUSE_BUTTON6DOWN,
1168	MOUSE_BUTTON7DOWN,
1169    };
1170    static int           pBufP = 0;
1171    static unsigned char pBuf[8];
1172
1173    debug("received char 0x%x",(int)rBuf);
1174
1175    /*
1176     * Hack for resyncing: We check here for a package that is:
1177     *  a) illegal (detected by wrong data-package header)
1178     *  b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
1179     *  c) bad header-package
1180     *
1181     * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
1182     *       -128 are allowed, but since they are very seldom we can easily
1183     *       use them as package-header with no button pressed.
1184     * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
1185     *         0x80 is not valid as a header byte. For a PS/2 mouse we skip
1186     *         checking data bytes.
1187     *         For resyncing a PS/2 mouse we require the two most significant
1188     *         bits in the header byte to be 0. These are the overflow bits,
1189     *         and in case of an overflow we actually lose sync. Overflows
1190     *         are very rare, however, and we quickly gain sync again after
1191     *         an overflow condition. This is the best we can do. (Actually,
1192     *         we could use bit 0x08 in the header byte for resyncing, since
1193     *         that bit is supposed to be always on, but nobody told
1194     *         Microsoft...)
1195     */
1196
1197    if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
1198	((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
1199    {
1200	pBufP = 0;		/* skip package */
1201    }
1202
1203    if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
1204	return 0;
1205
1206    /* is there an extra data byte? */
1207    if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
1208    {
1209	/*
1210	 * Hack for Logitech MouseMan Mouse - Middle button
1211	 *
1212	 * Unfortunately this mouse has variable length packets: the standard
1213	 * Microsoft 3 byte packet plus an optional 4th byte whenever the
1214	 * middle button status changes.
1215	 *
1216	 * We have already processed the standard packet with the movement
1217	 * and button info.  Now post an event message with the old status
1218	 * of the left and right buttons and the updated middle button.
1219	 */
1220
1221	/*
1222	 * Even worse, different MouseMen and TrackMen differ in the 4th
1223	 * byte: some will send 0x00/0x20, others 0x01/0x21, or even
1224	 * 0x02/0x22, so I have to strip off the lower bits.
1225         *
1226         * [JCH-96/01/21]
1227         * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
1228         * and it is activated by tapping the glidepad with the finger! 8^)
1229         * We map it to bit bit3, and the reverse map in xf86Events just has
1230         * to be extended so that it is identified as Button 4. The lower
1231         * half of the reverse-map may remain unchanged.
1232	 */
1233
1234        /*
1235	 * [KY-97/08/03]
1236	 * Receive the fourth byte only when preceeding three bytes have
1237	 * been detected (pBufP >= cur_proto[4]).  In the previous
1238	 * versions, the test was pBufP == 0; thus, we may have mistakingly
1239	 * received a byte even if we didn't see anything preceeding
1240	 * the byte.
1241	 */
1242
1243	if ((rBuf & cur_proto[5]) != cur_proto[6]) {
1244            pBufP = 0;
1245	    return 0;
1246	}
1247
1248	switch (rodent.rtype) {
1249#if notyet
1250	case MOUSE_PROTO_MARIQUA:
1251	    /*
1252	     * This mouse has 16! buttons in addition to the standard
1253	     * three of them.  They return 0x10 though 0x1f in the
1254	     * so-called `ten key' mode and 0x30 though 0x3f in the
1255	     * `function key' mode.  As there are only 31 bits for
1256	     * button state (including the standard three), we ignore
1257	     * the bit 0x20 and don't distinguish the two modes.
1258	     */
1259	    act->dx = act->dy = act->dz = 0;
1260	    act->obutton = act->button;
1261	    rBuf &= 0x1f;
1262	    act->button = (1 << (rBuf - 13))
1263                | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1264	    /*
1265	     * FIXME: this is a button "down" event. There needs to be
1266	     * a corresponding button "up" event... XXX
1267	     */
1268	    break;
1269#endif /* notyet */
1270
1271	/*
1272	 * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
1273	 * always send the fourth byte, whereas the fourth byte is
1274	 * optional for GlidePoint and ThinkingMouse. The fourth byte
1275	 * is also optional for MouseMan+ and FirstMouse+ in their
1276	 * native mode. It is always sent if they are in the IntelliMouse
1277	 * compatible mode.
1278	 */
1279	case MOUSE_PROTO_INTELLI:	/* IntelliMouse, NetMouse, Mie Mouse,
1280					   MouseMan+ */
1281	    act->dx = act->dy = 0;
1282	    act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
1283	    act->obutton = act->button;
1284	    act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1285		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1286	    break;
1287
1288	default:
1289	    act->dx = act->dy = act->dz = 0;
1290	    act->obutton = act->button;
1291	    act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1292		| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1293	    break;
1294	}
1295
1296	act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1297	    | (act->obutton ^ act->button);
1298        pBufP = 0;
1299	return act->flags;
1300    }
1301
1302    if (pBufP >= cur_proto[4])
1303	pBufP = 0;
1304    pBuf[pBufP++] = rBuf;
1305    if (pBufP != cur_proto[4])
1306	return 0;
1307
1308    /*
1309     * assembly full package
1310     */
1311
1312    debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
1313	cur_proto[4],
1314	pBuf[0], pBuf[1], pBuf[2], pBuf[3],
1315	pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
1316
1317    act->dz = 0;
1318    act->obutton = act->button;
1319    switch (rodent.rtype)
1320    {
1321    case MOUSE_PROTO_MS:		/* Microsoft */
1322    case MOUSE_PROTO_LOGIMOUSEMAN:	/* MouseMan/TrackMan */
1323	if (rodent.flags & ChordMiddle)
1324	    act->button = ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
1325		? MOUSE_BUTTON2DOWN
1326		: butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1327	else
1328	    act->button = (act->obutton & MOUSE_BUTTON2DOWN)
1329		| butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1330	act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1331	act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1332	break;
1333
1334    case MOUSE_PROTO_GLIDEPOINT:	/* GlidePoint */
1335    case MOUSE_PROTO_THINK:		/* ThinkingMouse */
1336    case MOUSE_PROTO_INTELLI:		/* IntelliMouse, NetMouse, Mie Mouse,
1337					   MouseMan+ */
1338	act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
1339            | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1340	act->dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1341	act->dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
1342	break;
1343
1344    case MOUSE_PROTO_MSC:		/* MouseSystems Corp */
1345#if notyet
1346    case MOUSE_PROTO_MARIQUA:		/* Mariqua */
1347#endif
1348	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1349	act->dx =    (char)(pBuf[1]) + (char)(pBuf[3]);
1350	act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
1351	break;
1352
1353    case MOUSE_PROTO_HITTAB:		/* MM HitTablet */
1354	act->button = butmaphit[pBuf[0] & 0x07];
1355	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1356	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1357	break;
1358
1359    case MOUSE_PROTO_MM:		/* MM Series */
1360    case MOUSE_PROTO_LOGI:		/* Logitech Mice */
1361	act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
1362	act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ?   pBuf[1] : - pBuf[1];
1363	act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] :   pBuf[2];
1364	break;
1365
1366    case MOUSE_PROTO_BUS:		/* Bus */
1367    case MOUSE_PROTO_INPORT:		/* InPort */
1368	act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
1369	act->dx =   (char)pBuf[1];
1370	act->dy = - (char)pBuf[2];
1371	break;
1372
1373    case MOUSE_PROTO_PS2:		/* PS/2 */
1374	act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
1375	act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ?    pBuf[1] - 256  :  pBuf[1];
1376	act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ?  -(pBuf[2] - 256) : -pBuf[2];
1377	/*
1378	 * Moused usually operates the psm driver at the operation level 1
1379	 * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
1380	 * The following code takes effect only when the user explicitly
1381	 * requets the level 2 at which wheel movement and additional button
1382	 * actions are encoded in model-dependent formats. At the level 0
1383	 * the following code is no-op because the psm driver says the model
1384	 * is MOUSE_MODEL_GENERIC.
1385	 */
1386	switch (rodent.hw.model) {
1387	case MOUSE_MODEL_INTELLI:
1388	case MOUSE_MODEL_NET:
1389	    /* wheel data is in the fourth byte */
1390	    act->dz = (char)pBuf[3];
1391	    break;
1392	case MOUSE_MODEL_MOUSEMANPLUS:
1393	    if ((pBuf[0] & ~MOUSE_PS2_BUTTONS) == 0xc8) {
1394		/* the extended data packet encodes button and wheel events */
1395		act->dx = act->dy = 0;
1396		act->dz = (pBuf[1] & MOUSE_PS2PLUS_ZNEG)
1397		    ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
1398		act->button |= ((pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
1399		    ? MOUSE_BUTTON4DOWN : 0);
1400	    } else {
1401		/* preserve button states */
1402		act->button |= act->obutton & MOUSE_EXTBUTTONS;
1403	    }
1404	    break;
1405	case MOUSE_MODEL_GLIDEPOINT:
1406	    /* `tapping' action */
1407	    act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
1408	    break;
1409	case MOUSE_MODEL_NETSCROLL:
1410	    /* three addtional bytes encode button and wheel events */
1411	    act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
1412		? MOUSE_BUTTON4DOWN : 0;
1413	    act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
1414	    break;
1415	case MOUSE_MODEL_THINK:
1416	    /* the fourth button state in the first byte */
1417	    act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
1418	    break;
1419	case MOUSE_MODEL_GENERIC:
1420	default:
1421	    break;
1422	}
1423	break;
1424
1425    case MOUSE_PROTO_SYSMOUSE:		/* sysmouse */
1426	act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
1427	act->dx =    (char)(pBuf[1]) + (char)(pBuf[3]);
1428	act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
1429	if (rodent.level == 1) {
1430	    act->dz = ((char)(pBuf[5] << 1) + (char)(pBuf[6] << 1))/2;
1431	    act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
1432	}
1433	break;
1434
1435    default:
1436	return 0;
1437    }
1438    /*
1439     * We don't reset pBufP here yet, as there may be an additional data
1440     * byte in some protocols. See above.
1441     */
1442
1443    /* has something changed? */
1444    act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1445	| (act->obutton ^ act->button);
1446
1447    if (rodent.flags & Emulate3Button) {
1448	if (((act->flags & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1449	        == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1450	    && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1451	        == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) {
1452	    act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN);
1453	    act->button |= MOUSE_BUTTON2DOWN;
1454	} else if ((act->obutton & MOUSE_BUTTON2DOWN)
1455	    && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
1456	        != (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) {
1457	    act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN
1458			       | MOUSE_BUTTON3DOWN);
1459	}
1460	act->flags &= MOUSE_POSCHANGED;
1461	act->flags |= act->obutton ^ act->button;
1462    }
1463
1464    return act->flags;
1465}
1466
1467/* phisical to logical button mapping */
1468static int p2l[MOUSE_MAXBUTTON] = {
1469    MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
1470    MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
1471    0x00000100,        0x00000200,        0x00000400,        0x00000800,
1472    0x00001000,        0x00002000,        0x00004000,        0x00008000,
1473    0x00010000,        0x00020000,        0x00040000,        0x00080000,
1474    0x00100000,        0x00200000,        0x00400000,        0x00800000,
1475    0x01000000,        0x02000000,        0x04000000,        0x08000000,
1476    0x10000000,        0x20000000,        0x40000000,
1477};
1478
1479static char *
1480skipspace(char *s)
1481{
1482    while(isspace(*s))
1483	++s;
1484    return s;
1485}
1486
1487static int
1488r_installmap(char *arg)
1489{
1490    int pbutton;
1491    int lbutton;
1492    char *s;
1493
1494    while (*arg) {
1495	arg = skipspace(arg);
1496	s = arg;
1497	while (isdigit(*arg))
1498	    ++arg;
1499	arg = skipspace(arg);
1500	if ((arg <= s) || (*arg != '='))
1501	    return FALSE;
1502	lbutton = atoi(s);
1503
1504	arg = skipspace(++arg);
1505	s = arg;
1506	while (isdigit(*arg))
1507	    ++arg;
1508	if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
1509	    return FALSE;
1510	pbutton = atoi(s);
1511
1512	if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
1513	    return FALSE;
1514	if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
1515	    return FALSE;
1516	p2l[pbutton - 1] = 1 << (lbutton - 1);
1517    }
1518
1519    return TRUE;
1520}
1521
1522static void
1523r_map(mousestatus_t *act1, mousestatus_t *act2)
1524{
1525    register int pb;
1526    register int pbuttons;
1527    int lbuttons;
1528
1529    pbuttons = act1->button;
1530    lbuttons = 0;
1531
1532    act2->obutton = act2->button;
1533    act2->dx = act1->dx;
1534    act2->dy = act1->dy;
1535    act2->dz = act1->dz;
1536
1537    switch (rodent.zmap) {
1538    case 0:	/* do nothing */
1539	break;
1540    case MOUSE_XAXIS:
1541	if (act1->dz != 0) {
1542	    act2->dx = act1->dz;
1543	    act2->dz = 0;
1544	}
1545	break;
1546    case MOUSE_YAXIS:
1547	if (act1->dz != 0) {
1548	    act2->dy = act1->dz;
1549	    act2->dz = 0;
1550	}
1551	break;
1552    default:	/* buttons */
1553	pbuttons &= ~(rodent.zmap | (rodent.zmap << 1));
1554	if (act1->dz < 0)
1555	    pbuttons |= rodent.zmap;
1556	else if (act1->dz > 0)
1557	    pbuttons |= (rodent.zmap << 1);
1558	act2->dz = 0;
1559	break;
1560    }
1561
1562    for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
1563	lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
1564	pbuttons >>= 1;
1565    }
1566    act2->button = lbuttons;
1567
1568    act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
1569	| (act2->obutton ^ act2->button);
1570}
1571
1572static void
1573r_click(mousestatus_t *act)
1574{
1575    struct mouse_info mouse;
1576    struct timeval tv;
1577    struct timeval tv1;
1578    struct timeval tv2;
1579    struct timezone tz;
1580    int button;
1581    int mask;
1582    int i;
1583
1584    mask = act->flags & MOUSE_BUTTONS;
1585    if (mask == 0)
1586	return;
1587
1588    gettimeofday(&tv1, &tz);
1589    tv2.tv_sec = rodent.clickthreshold/1000;
1590    tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
1591    timersub(&tv1, &tv2, &tv);
1592    debug("tv:  %ld %ld", tv.tv_sec, tv.tv_usec);
1593    button = MOUSE_BUTTON1DOWN;
1594    for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
1595        if (mask & 1) {
1596            if (act->button & button) {
1597                /* the button is down */
1598    		debug("  :  %ld %ld",
1599		    buttonstate[i].tv.tv_sec, buttonstate[i].tv.tv_usec);
1600		if (timercmp(&tv, &buttonstate[i].tv, >)) {
1601                    buttonstate[i].tv.tv_sec = 0;
1602                    buttonstate[i].tv.tv_usec = 0;
1603                    buttonstate[i].count = 1;
1604                } else {
1605                    ++buttonstate[i].count;
1606                }
1607	        mouse.u.event.value = buttonstate[i].count;
1608            } else {
1609                /* the button is up */
1610                buttonstate[i].tv = tv1;
1611	        mouse.u.event.value = 0;
1612            }
1613	    mouse.operation = MOUSE_BUTTON_EVENT;
1614	    mouse.u.event.id = button;
1615	    if (debug < 2)
1616	        ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
1617	    debug("button %d  count %d", i + 1, mouse.u.event.value);
1618        }
1619	button <<= 1;
1620	mask >>= 1;
1621    }
1622}
1623
1624/* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
1625/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
1626/*
1627 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
1628 *
1629 * Permission to use, copy, modify, distribute, and sell this software and its
1630 * documentation for any purpose is hereby granted without fee, provided that
1631 * the above copyright notice appear in all copies and that both that
1632 * copyright notice and this permission notice appear in supporting
1633 * documentation, and that the name of David Dawes
1634 * not be used in advertising or publicity pertaining to distribution of
1635 * the software without specific, written prior permission.
1636 * David Dawes makes no representations about the suitability of this
1637 * software for any purpose.  It is provided "as is" without express or
1638 * implied warranty.
1639 *
1640 * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
1641 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
1642 * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
1643 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
1644 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
1645 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1646 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1647 *
1648 */
1649
1650
1651static void
1652setmousespeed(int old, int new, unsigned cflag)
1653{
1654	struct termios tty;
1655	char *c;
1656
1657	if (tcgetattr(rodent.mfd, &tty) < 0)
1658	{
1659		logwarn("unable to get status of mouse fd", 0);
1660		return;
1661	}
1662
1663	tty.c_iflag = IGNBRK | IGNPAR;
1664	tty.c_oflag = 0;
1665	tty.c_lflag = 0;
1666	tty.c_cflag = (tcflag_t)cflag;
1667	tty.c_cc[VTIME] = 0;
1668	tty.c_cc[VMIN] = 1;
1669
1670	switch (old)
1671	{
1672	case 9600:
1673		cfsetispeed(&tty, B9600);
1674		cfsetospeed(&tty, B9600);
1675		break;
1676	case 4800:
1677		cfsetispeed(&tty, B4800);
1678		cfsetospeed(&tty, B4800);
1679		break;
1680	case 2400:
1681		cfsetispeed(&tty, B2400);
1682		cfsetospeed(&tty, B2400);
1683		break;
1684	case 1200:
1685	default:
1686		cfsetispeed(&tty, B1200);
1687		cfsetospeed(&tty, B1200);
1688	}
1689
1690	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
1691	{
1692		logwarn("unable to set status of mouse fd", 0);
1693		return;
1694	}
1695
1696	switch (new)
1697	{
1698	case 9600:
1699		c = "*q";
1700		cfsetispeed(&tty, B9600);
1701		cfsetospeed(&tty, B9600);
1702		break;
1703	case 4800:
1704		c = "*p";
1705		cfsetispeed(&tty, B4800);
1706		cfsetospeed(&tty, B4800);
1707		break;
1708	case 2400:
1709		c = "*o";
1710		cfsetispeed(&tty, B2400);
1711		cfsetospeed(&tty, B2400);
1712		break;
1713	case 1200:
1714	default:
1715		c = "*n";
1716		cfsetispeed(&tty, B1200);
1717		cfsetospeed(&tty, B1200);
1718	}
1719
1720	if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
1721	    || rodent.rtype == MOUSE_PROTO_LOGI)
1722	{
1723		if (write(rodent.mfd, c, 2) != 2)
1724		{
1725			logwarn("unable to write to mouse fd", 0);
1726			return;
1727		}
1728	}
1729	usleep(100000);
1730
1731	if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
1732		logwarn("unable to set status of mouse fd", 0);
1733}
1734
1735/*
1736 * PnP COM device support
1737 *
1738 * It's a simplistic implementation, but it works :-)
1739 * KY, 31/7/97.
1740 */
1741
1742/*
1743 * Try to elicit a PnP ID as described in
1744 * Microsoft, Hayes: "Plug and Play External COM Device Specification,
1745 * rev 1.00", 1995.
1746 *
1747 * The routine does not fully implement the COM Enumerator as par Section
1748 * 2.1 of the document.  In particular, we don't have idle state in which
1749 * the driver software monitors the com port for dynamic connection or
1750 * removal of a device at the port, because `moused' simply quits if no
1751 * device is found.
1752 *
1753 * In addition, as PnP COM device enumeration procedure slightly has
1754 * changed since its first publication, devices which follow earlier
1755 * revisions of the above spec. may fail to respond if the rev 1.0
1756 * procedure is used. XXX
1757 */
1758static int
1759pnpgets(char *buf)
1760{
1761    struct timeval timeout;
1762    fd_set fds;
1763    int i;
1764    char c;
1765
1766#if 0
1767    /*
1768     * This is the procedure described in rev 1.0 of PnP COM device spec.
1769     * Unfortunately, some devices which comform to earlier revisions of
1770     * the spec gets confused and do not return the ID string...
1771     */
1772
1773    /* port initialization (2.1.2) */
1774    ioctl(rodent.mfd, TIOCMGET, &i);
1775    i |= TIOCM_DTR;		/* DTR = 1 */
1776    i &= ~TIOCM_RTS;		/* RTS = 0 */
1777    ioctl(rodent.mfd, TIOCMSET, &i);
1778    usleep(200000);
1779    if ((ioctl(rodent.mfd, TIOCMGET, &i) == -1) || ((i & TIOCM_DSR) == 0))
1780	goto disconnect_idle;
1781
1782    /* port setup, 1st phase (2.1.3) */
1783    setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
1784    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
1785    ioctl(rodent.mfd, TIOCMBIC, &i);
1786    usleep(200000);
1787    i = TIOCM_DTR;		/* DTR = 1, RTS = 0 */
1788    ioctl(rodent.mfd, TIOCMBIS, &i);
1789    usleep(200000);
1790
1791    /* wait for response, 1st phase (2.1.4) */
1792    i = FREAD;
1793    ioctl(rodent.mfd, TIOCFLUSH, &i);
1794    i = TIOCM_RTS;		/* DTR = 1, RTS = 1 */
1795    ioctl(rodent.mfd, TIOCMBIS, &i);
1796
1797    /* try to read something */
1798    FD_ZERO(&fds);
1799    FD_SET(rodent.mfd, &fds);
1800    timeout.tv_sec = 0;
1801    timeout.tv_usec = 200000;
1802    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0) {
1803
1804	/* port setup, 2nd phase (2.1.5) */
1805        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 0, RTS = 0 */
1806        ioctl(rodent.mfd, TIOCMBIC, &i);
1807        usleep(200000);
1808
1809	/* wait for respose, 2nd phase (2.1.6) */
1810        i = FREAD;
1811        ioctl(rodent.mfd, TIOCFLUSH, &i);
1812        i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
1813        ioctl(rodent.mfd, TIOCMBIS, &i);
1814
1815        /* try to read something */
1816        FD_ZERO(&fds);
1817        FD_SET(rodent.mfd, &fds);
1818        timeout.tv_sec = 0;
1819        timeout.tv_usec = 200000;
1820        if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
1821	    goto connect_idle;
1822    }
1823#else
1824    /*
1825     * This is a simplified procedure; it simply toggles RTS.
1826     */
1827
1828    ioctl(rodent.mfd, TIOCMGET, &i);
1829    i |= TIOCM_DTR;		/* DTR = 1 */
1830    i &= ~TIOCM_RTS;		/* RTS = 0 */
1831    ioctl(rodent.mfd, TIOCMSET, &i);
1832    usleep(200000);
1833
1834    setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
1835
1836    /* wait for respose */
1837    i = FREAD;
1838    ioctl(rodent.mfd, TIOCFLUSH, &i);
1839    i = TIOCM_DTR | TIOCM_RTS;	/* DTR = 1, RTS = 1 */
1840    ioctl(rodent.mfd, TIOCMBIS, &i);
1841
1842    /* try to read something */
1843    FD_ZERO(&fds);
1844    FD_SET(rodent.mfd, &fds);
1845    timeout.tv_sec = 0;
1846    timeout.tv_usec = 200000;
1847    if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
1848        goto connect_idle;
1849#endif
1850
1851    /* collect PnP COM device ID (2.1.7) */
1852    i = 0;
1853    usleep(200000);	/* the mouse must send `Begin ID' within 200msec */
1854    while (read(rodent.mfd, &c, 1) == 1) {
1855	/* we may see "M", or "M3..." before `Begin ID' */
1856        if ((c == 0x08) || (c == 0x28)) {	/* Begin ID */
1857	    buf[i++] = c;
1858	    break;
1859        }
1860        debug("%c %02x", c, c);
1861    }
1862    if (i <= 0) {
1863	/* we haven't seen `Begin ID' in time... */
1864	goto connect_idle;
1865    }
1866
1867    ++c;			/* make it `End ID' */
1868    for (;;) {
1869        FD_ZERO(&fds);
1870        FD_SET(rodent.mfd, &fds);
1871        timeout.tv_sec = 0;
1872        timeout.tv_usec = 200000;
1873        if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
1874	    break;
1875
1876	read(rodent.mfd, &buf[i], 1);
1877        if (buf[i++] == c)	/* End ID */
1878	    break;
1879	if (i >= 256)
1880	    break;
1881    }
1882    /* string may not be human readable... */
1883    debug("'%-*.*s'", i, i, buf);
1884    if (buf[i - 1] != c)
1885	goto connect_idle;
1886    return i;
1887
1888    /*
1889     * According to PnP spec, we should set DTR = 1 and RTS = 0 while
1890     * in idle state.  But, `moused' shall set DTR = RTS = 1 and proceed,
1891     * assuming there is something at the port even if it didn't
1892     * respond to the PnP enumeration procedure.
1893     */
1894disconnect_idle:
1895    i = TIOCM_DTR | TIOCM_RTS;		/* DTR = 1, RTS = 1 */
1896    ioctl(rodent.mfd, TIOCMBIS, &i);
1897connect_idle:
1898    return 0;
1899}
1900
1901static int
1902pnpparse(pnpid_t *id, char *buf, int len)
1903{
1904    char s[3];
1905    int offset;
1906    int sum = 0;
1907    int i, j;
1908
1909    id->revision = 0;
1910    id->eisaid = NULL;
1911    id->serial = NULL;
1912    id->class = NULL;
1913    id->compat = NULL;
1914    id->description = NULL;
1915    id->neisaid = 0;
1916    id->nserial = 0;
1917    id->nclass = 0;
1918    id->ncompat = 0;
1919    id->ndescription = 0;
1920
1921    offset = 0x28 - buf[0];
1922
1923    /* calculate checksum */
1924    for (i = 0; i < len - 3; ++i) {
1925	sum += buf[i];
1926	buf[i] += offset;
1927    }
1928    sum += buf[len - 1];
1929    for (; i < len; ++i)
1930	buf[i] += offset;
1931    debug("PnP ID string: '%*.*s'", len, len, buf);
1932
1933    /* revision */
1934    buf[1] -= offset;
1935    buf[2] -= offset;
1936    id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
1937    debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
1938
1939    /* EISA vender and product ID */
1940    id->eisaid = &buf[3];
1941    id->neisaid = 7;
1942
1943    /* option strings */
1944    i = 10;
1945    if (buf[i] == '\\') {
1946        /* device serial # */
1947        for (j = ++i; i < len; ++i) {
1948            if (buf[i] == '\\')
1949		break;
1950        }
1951	if (i >= len)
1952	    i -= 3;
1953	if (i - j == 8) {
1954            id->serial = &buf[j];
1955            id->nserial = 8;
1956	}
1957    }
1958    if (buf[i] == '\\') {
1959        /* PnP class */
1960        for (j = ++i; i < len; ++i) {
1961            if (buf[i] == '\\')
1962		break;
1963        }
1964	if (i >= len)
1965	    i -= 3;
1966	if (i > j + 1) {
1967            id->class = &buf[j];
1968            id->nclass = i - j;
1969        }
1970    }
1971    if (buf[i] == '\\') {
1972	/* compatible driver */
1973        for (j = ++i; i < len; ++i) {
1974            if (buf[i] == '\\')
1975		break;
1976        }
1977	/*
1978	 * PnP COM spec prior to v0.96 allowed '*' in this field,
1979	 * it's not allowed now; just igore it.
1980	 */
1981	if (buf[j] == '*')
1982	    ++j;
1983	if (i >= len)
1984	    i -= 3;
1985	if (i > j + 1) {
1986            id->compat = &buf[j];
1987            id->ncompat = i - j;
1988        }
1989    }
1990    if (buf[i] == '\\') {
1991	/* product description */
1992        for (j = ++i; i < len; ++i) {
1993            if (buf[i] == ';')
1994		break;
1995        }
1996	if (i >= len)
1997	    i -= 3;
1998	if (i > j + 1) {
1999            id->description = &buf[j];
2000            id->ndescription = i - j;
2001        }
2002    }
2003
2004    /* checksum exists if there are any optional fields */
2005    if ((id->nserial > 0) || (id->nclass > 0)
2006	|| (id->ncompat > 0) || (id->ndescription > 0)) {
2007        debug("PnP checksum: 0x%X", sum);
2008        sprintf(s, "%02X", sum & 0x0ff);
2009        if (strncmp(s, &buf[len - 3], 2) != 0) {
2010#if 0
2011            /*
2012	     * I found some mice do not comply with the PnP COM device
2013	     * spec regarding checksum... XXX
2014	     */
2015            logwarnx("PnP checksum error", 0);
2016	    return FALSE;
2017#endif
2018        }
2019    }
2020
2021    return TRUE;
2022}
2023
2024static symtab_t *
2025pnpproto(pnpid_t *id)
2026{
2027    symtab_t *t;
2028    int i, j;
2029
2030    if (id->nclass > 0)
2031	if (strncmp(id->class, "MOUSE", id->nclass) != 0)
2032	    /* this is not a mouse! */
2033	    return NULL;
2034
2035    if (id->neisaid > 0) {
2036        t = gettoken(pnpprod, id->eisaid, id->neisaid);
2037	if (t->val != MOUSE_PROTO_UNKNOWN)
2038            return t;
2039    }
2040
2041    /*
2042     * The 'Compatible drivers' field may contain more than one
2043     * ID separated by ','.
2044     */
2045    if (id->ncompat <= 0)
2046	return NULL;
2047    for (i = 0; i < id->ncompat; ++i) {
2048        for (j = i; id->compat[i] != ','; ++i)
2049            if (i >= id->ncompat)
2050		break;
2051        if (i > j) {
2052            t = gettoken(pnpprod, id->compat + j, i - j);
2053	    if (t->val != MOUSE_PROTO_UNKNOWN)
2054                return t;
2055	}
2056    }
2057
2058    return NULL;
2059}
2060
2061/* name/val mapping */
2062
2063static symtab_t *
2064gettoken(symtab_t *tab, char *s, int len)
2065{
2066    int i;
2067
2068    for (i = 0; tab[i].name != NULL; ++i) {
2069	if (strncmp(tab[i].name, s, len) == 0)
2070	    break;
2071    }
2072    return &tab[i];
2073}
2074
2075static char *
2076gettokenname(symtab_t *tab, int val)
2077{
2078    int i;
2079
2080    for (i = 0; tab[i].name != NULL; ++i) {
2081	if (tab[i].val == val)
2082	    return tab[i].name;
2083    }
2084    return NULL;
2085}
2086