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