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