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