nrelease - fix/improve livecd
[dragonfly.git] / usr.sbin / moused / moused.c
CommitLineData
984263bc
MD
1/**
2 ** Copyright (c) 1995 Michael Smith, All rights reserved.
3 **
4 ** Redistribution and use in source and binary forms, with or without
5 ** modification, are permitted provided that the following conditions
6 ** are met:
7 ** 1. Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer as
9 ** the first lines of this file unmodified.
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 ** notice, this list of conditions and the following disclaimer in the
12 ** documentation and/or other materials provided with the distribution.
13 ** 3. All advertising materials mentioning features or use of this software
14 ** must display the following acknowledgment:
15 ** This product includes software developed by Michael Smith.
16 ** 4. The name of the author may not be used to endorse or promote products
17 ** derived from this software without specific prior written permission.
18 **
19 **
20 ** THIS SOFTWARE IS PROVIDED BY Michael Smith ``AS IS'' AND ANY
21 ** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith BE LIABLE FOR
24 ** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29 ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 ** EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **
32 **/
33
34/**
35 ** MOUSED.C
36 **
37 ** Mouse daemon : listens to a serial port, the bus mouse interface, or
918cebb3 38 ** the PS/2 mouse port for mouse data stream, interprets data and passes
984263bc
MD
39 ** ioctls off to the console driver.
40 **
41 ** The mouse interface functions are derived closely from the mouse
42 ** handler in the XFree86 X server. Many thanks to the XFree86 people
43 ** for their great work!
918cebb3 44 **
984263bc
MD
45 **/
46
1de703da 47/*
918cebb3 48 * $FreeBSD: src/usr.sbin/moused/moused.c,v 1.69 2005/01/06 08:38:29 philip Exp $
1de703da 49 */
5868d2b9 50
918cebb3 51#include <machine/console.h>
62770245 52#include <sys/mouse.h>
918cebb3
MS
53#include <sys/param.h>
54#include <sys/consio.h>
55#include <sys/linker.h>
56#include <sys/module.h>
57#include <sys/socket.h>
58#include <sys/stat.h>
59#include <sys/time.h>
60#include <sys/un.h>
61
984263bc
MD
62#include <ctype.h>
63#include <err.h>
64#include <errno.h>
65#include <fcntl.h>
66#include <limits.h>
918cebb3
MS
67#include <setjmp.h>
68#include <signal.h>
69#include <stdarg.h>
984263bc
MD
70#include <stdio.h>
71#include <stdlib.h>
984263bc 72#include <string.h>
984263bc 73#include <syslog.h>
918cebb3 74#include <termios.h>
984263bc
MD
75#include <unistd.h>
76
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 */
918cebb3 81#define DFLT_SCROLLTHRESHOLD 3 /* 3 pixels */
984263bc
MD
82
83/* Abort 3-button emulation delay after this many movement events. */
84#define BUTTON2_MAXMOVE 3
85
86#define TRUE 1
87#define FALSE 0
88
89#define MOUSE_XAXIS (-1)
90#define MOUSE_YAXIS (-2)
91
92/* Logitech PS2++ protocol */
93#define MOUSE_PS2PLUS_CHECKBITS(b) \
94 ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
95#define MOUSE_PS2PLUS_PACKET_TYPE(b) \
96 (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
97
98#define ChordMiddle 0x0001
99#define Emulate3Button 0x0002
100#define ClearDTR 0x0004
101#define ClearRTS 0x0008
102#define NoPnP 0x0010
918cebb3
MS
103#define VirtualScroll 0x0020
104
984263bc
MD
105#define ID_NONE 0
106#define ID_PORT 1
107#define ID_IF 2
918cebb3 108#define ID_TYPE 4
984263bc
MD
109#define ID_MODEL 8
110#define ID_ALL (ID_PORT | ID_IF | ID_TYPE | ID_MODEL)
111
918cebb3
MS
112#define debug(fmt, args...) do { \
113 if (debug && nodaemon) \
114 warnx(fmt, ##args); \
115} while (0)
984263bc 116
918cebb3
MS
117#define logerr(e, fmt, args...) do { \
118 log_or_warn(LOG_DAEMON | LOG_ERR, errno, fmt, ##args); \
119 exit(e); \
120} while (0)
984263bc 121
918cebb3
MS
122#define logerrx(e, fmt, args...) do { \
123 log_or_warn(LOG_DAEMON | LOG_ERR, 0, fmt, ##args); \
124 exit(e); \
125} while (0)
984263bc 126
918cebb3
MS
127#define logwarn(fmt, args...) \
128 log_or_warn(LOG_DAEMON | LOG_WARNING, errno, fmt, ##args)
984263bc 129
918cebb3
MS
130#define logwarnx(fmt, args...) \
131 log_or_warn(LOG_DAEMON | LOG_WARNING, 0, fmt, ##args)
984263bc
MD
132
133/* structures */
134
135/* symbol table entry */
136typedef struct {
137 char *name;
138 int val;
139 int val2;
140} symtab_t;
141
142/* serial PnP ID string */
143typedef struct {
144 int revision; /* PnP revision, 100 for 1.00 */
145 char *eisaid; /* EISA ID including mfr ID and product ID */
146 char *serial; /* serial No, optional */
147 char *class; /* device class, optional */
148 char *compat; /* list of compatible drivers, optional */
149 char *description; /* product description, optional */
150 int neisaid; /* length of the above fields... */
151 int nserial;
152 int nclass;
153 int ncompat;
154 int ndescription;
155} pnpid_t;
156
157/* global variables */
158
159int debug = 0;
160int nodaemon = FALSE;
161int background = FALSE;
162int identify = ID_NONE;
163int extioctl = FALSE;
164char *pidfile = "/var/run/moused.pid";
165
918cebb3
MS
166#define SCROLL_NOTSCROLLING 0
167#define SCROLL_PREPARE 1
168#define SCROLL_SCROLLING 2
169
170static int scroll_state;
171static int scroll_movement;
172
984263bc
MD
173/* local variables */
174
175/* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
176static symtab_t rifs[] = {
177 { "serial", MOUSE_IF_SERIAL },
178 { "bus", MOUSE_IF_BUS },
179 { "inport", MOUSE_IF_INPORT },
180 { "ps/2", MOUSE_IF_PS2 },
181 { "sysmouse", MOUSE_IF_SYSMOUSE },
182 { "usb", MOUSE_IF_USB },
183 { NULL, MOUSE_IF_UNKNOWN },
184};
185
186/* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
187static char *rnames[] = {
188 "microsoft",
189 "mousesystems",
190 "logitech",
191 "mmseries",
192 "mouseman",
193 "busmouse",
194 "inportmouse",
195 "ps/2",
196 "mmhitab",
197 "glidepoint",
198 "intellimouse",
199 "thinkingmouse",
200 "sysmouse",
201 "x10mouseremote",
202 "kidspad",
203 "versapad",
204 "jogdial",
2cc577c1 205#if 0 /* not yet */
984263bc
MD
206 "mariqua",
207#endif
208 NULL
209};
210
211/* models */
212static symtab_t rmodels[] = {
213 { "NetScroll", MOUSE_MODEL_NETSCROLL },
214 { "NetMouse/NetScroll Optical", MOUSE_MODEL_NET },
215 { "GlidePoint", MOUSE_MODEL_GLIDEPOINT },
216 { "ThinkingMouse", MOUSE_MODEL_THINK },
217 { "IntelliMouse", MOUSE_MODEL_INTELLI },
218 { "EasyScroll/SmartScroll", MOUSE_MODEL_EASYSCROLL },
219 { "MouseMan+", MOUSE_MODEL_MOUSEMANPLUS },
220 { "Kidspad", MOUSE_MODEL_KIDSPAD },
221 { "VersaPad", MOUSE_MODEL_VERSAPAD },
222 { "IntelliMouse Explorer", MOUSE_MODEL_EXPLORER },
223 { "4D Mouse", MOUSE_MODEL_4D },
224 { "4D+ Mouse", MOUSE_MODEL_4DPLUS },
dfd2215c 225 { "Synaptics Touchpad", MOUSE_MODEL_SYNAPTICS },
984263bc 226 { "generic", MOUSE_MODEL_GENERIC },
918cebb3 227 { NULL, MOUSE_MODEL_UNKNOWN },
984263bc
MD
228};
229
230/* PnP EISA/product IDs */
231static 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 */
918cebb3 249 { "KYEEZ00", MOUSE_PROTO_MS, MOUSE_MODEL_EASYSCROLL },
984263bc
MD
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 },
266
267 /* MS bus */
268 { "PNP0F00", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
269 /* MS serial */
918cebb3 270 { "PNP0F01", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
984263bc 271 /* MS InPort */
918cebb3 272 { "PNP0F02", MOUSE_PROTO_INPORT, MOUSE_MODEL_GENERIC },
984263bc 273 /* MS PS/2 */
918cebb3 274 { "PNP0F03", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
984263bc
MD
275 /*
276 * EzScroll returns PNP0F04 in the compatible device field; but it
277 * doesn't look compatible... XXX
278 */
918cebb3
MS
279 /* MouseSystems */
280 { "PNP0F04", MOUSE_PROTO_MSC, MOUSE_MODEL_GENERIC },
281 /* MouseSystems */
282 { "PNP0F05", MOUSE_PROTO_MSC, MOUSE_MODEL_GENERIC },
2cc577c1 283#if 0 /* not yet */
918cebb3
MS
284 /* Genius Mouse */
285 { "PNP0F06", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
286 /* Genius Mouse */
287 { "PNP0F07", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc
MD
288#endif
289 /* Logitech serial */
290 { "PNP0F08", MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
291 /* MS BallPoint serial */
918cebb3 292 { "PNP0F09", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
984263bc 293 /* MS PnP serial */
918cebb3 294 { "PNP0F0A", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
984263bc 295 /* MS PnP BallPoint serial */
918cebb3 296 { "PNP0F0B", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
984263bc 297 /* MS serial comatible */
918cebb3 298 { "PNP0F0C", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
984263bc 299 /* MS InPort comatible */
918cebb3 300 { "PNP0F0D", MOUSE_PROTO_INPORT, MOUSE_MODEL_GENERIC },
984263bc 301 /* MS PS/2 comatible */
918cebb3 302 { "PNP0F0E", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
984263bc 303 /* MS BallPoint comatible */
918cebb3 304 { "PNP0F0F", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
2cc577c1 305#if 0 /* not yet */
984263bc 306 /* TI QuickPort */
918cebb3 307 { "PNP0F10", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc
MD
308#endif
309 /* MS bus comatible */
918cebb3 310 { "PNP0F11", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
984263bc
MD
311 /* Logitech PS/2 */
312 { "PNP0F12", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
313 /* PS/2 */
314 { "PNP0F13", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
2cc577c1 315#if 0 /* not yet */
984263bc 316 /* MS Kids Mouse */
918cebb3 317 { "PNP0F14", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc 318#endif
918cebb3 319 /* Logitech bus */
984263bc 320 { "PNP0F15", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
2cc577c1 321#if 0 /* not yet */
984263bc 322 /* Logitech SWIFT */
918cebb3 323 { "PNP0F16", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc
MD
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 },
2cc577c1 331#if 0 /* not yet */
984263bc 332 /* Logitech SWIFT compatible */
918cebb3 333 { "PNP0F1A", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc 334 /* HP Omnibook */
918cebb3 335 { "PNP0F1B", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc 336 /* Compaq LTE TrackBall PS/2 */
918cebb3 337 { "PNP0F1C", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc 338 /* Compaq LTE TrackBall serial */
918cebb3 339 { "PNP0F1D", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc 340 /* MS Kidts Trackball */
918cebb3 341 { "PNP0F1E", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
984263bc
MD
342#endif
343 /* Interlink VersaPad */
344 { "LNK0001", MOUSE_PROTO_VERSAPAD, MOUSE_MODEL_VERSAPAD },
345
346 { NULL, MOUSE_PROTO_UNKNOWN, MOUSE_MODEL_GENERIC },
347};
348
349/* the table must be ordered by MOUSE_PROTO_XXX in mouse.h */
350static unsigned short rodentcflags[] =
351{
918cebb3
MS
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 */
984263bc
MD
357 0, /* Bus */
358 0, /* InPort */
359 0, /* PS/2 */
918cebb3
MS
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 */
984263bc 368 0, /* JogDial */
2cc577c1 369#if 0 /* not yet */
918cebb3 370 (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* Mariqua */
984263bc
MD
371#endif
372};
373
374static 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 */
918cebb3
MS
394 int scrollthreshold; /* Movement distance before virtual scrolling */
395} rodent = {
396 .flags = 0,
397 .portname = NULL,
398 .rtype = MOUSE_PROTO_UNKNOWN,
399 .level = -1,
400 .baudrate = 1200,
401 .rate = 0,
402 .resolution = MOUSE_RES_UNKNOWN,
403 .zmap = { 0, 0, 0, 0 },
404 .wmode = 0,
405 .mfd = -1,
406 .cfd = -1,
407 .mremsfd = -1,
408 .mremcfd = -1,
409 .clickthreshold = DFLT_CLICKTHRESHOLD,
410 .button2timeout = DFLT_BUTTON2TIMEOUT,
411 .accelx = 1.0,
412 .accely = 1.0,
413 .scrollthreshold = DFLT_SCROLLTHRESHOLD,
984263bc
MD
414};
415
416/* button status */
417struct button_state {
418 int count; /* 0: up, 1: single click, 2: double click,... */
419 struct timeval tv; /* timestamp on the last button event */
420};
421static struct button_state bstate[MOUSE_MAXBUTTON]; /* button state */
422static struct button_state *mstate[MOUSE_MAXBUTTON];/* mapped button st.*/
423static struct button_state zstate[4]; /* Z/W axis state */
424
425/* state machine for 3 button emulation */
426
427#define S0 0 /* start */
428#define S1 1 /* button 1 delayed down */
429#define S2 2 /* button 3 delayed down */
430#define S3 3 /* both buttons down -> button 2 down */
431#define S4 4 /* button 1 delayed up */
432#define S5 5 /* button 1 down */
433#define S6 6 /* button 3 down */
434#define S7 7 /* both buttons down */
435#define S8 8 /* button 3 delayed up */
436#define S9 9 /* button 1 or 3 up after S3 */
437
438#define A(b1, b3) (((b1) ? 2 : 0) | ((b3) ? 1 : 0))
439#define A_TIMEOUT 4
440#define S_DELAYED(st) (states[st].s[A_TIMEOUT] != (st))
441
442static struct {
443 int s[A_TIMEOUT + 1];
444 int buttons;
445 int mask;
446 int timeout;
447} states[10] = {
448 /* S0 */
449 { { S0, S2, S1, S3, S0 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
450 /* S1 */
451 { { S4, S2, S1, S3, S5 }, 0, ~MOUSE_BUTTON1DOWN, FALSE },
452 /* S2 */
453 { { S8, S2, S1, S3, S6 }, 0, ~MOUSE_BUTTON3DOWN, FALSE },
454 /* S3 */
455 { { S0, S9, S9, S3, S3 }, MOUSE_BUTTON2DOWN, ~0, FALSE },
456 /* S4 */
457 { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON1DOWN, ~0, TRUE },
458 /* S5 */
459 { { S0, S2, S5, S7, S5 }, MOUSE_BUTTON1DOWN, ~0, FALSE },
460 /* S6 */
461 { { S0, S6, S1, S7, S6 }, MOUSE_BUTTON3DOWN, ~0, FALSE },
462 /* S7 */
463 { { S0, S6, S5, S7, S7 }, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, ~0, FALSE },
464 /* S8 */
465 { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON3DOWN, ~0, TRUE },
466 /* S9 */
467 { { S0, S9, S9, S3, S9 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
468};
469static int mouse_button_state;
470static struct timeval mouse_button_state_tv;
471static int mouse_move_delayed;
472
473static jmp_buf env;
474
475/* function prototypes */
476
477static void moused(void);
478static void hup(int sig);
479static void cleanup(int sig);
480static void usage(void);
918cebb3
MS
481static void log_or_warn(int log_pri, int errnum, const char *fmt, ...)
482 __printflike(3, 4);
984263bc
MD
483
484static int r_identify(void);
485static char *r_if(int type);
486static char *r_name(int type);
487static char *r_model(int model);
488static void r_init(void);
489static int r_protocol(u_char b, mousestatus_t *act);
490static int r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans);
491static int r_installmap(char *arg);
492static void r_map(mousestatus_t *act1, mousestatus_t *act2);
493static void r_timestamp(mousestatus_t *act);
494static int r_timeout(void);
495static void r_click(mousestatus_t *act);
496static void setmousespeed(int old, int new, unsigned cflag);
497
498static int pnpwakeup1(void);
499static int pnpwakeup2(void);
500static int pnpgets(char *buf);
501static int pnpparse(pnpid_t *id, char *buf, int len);
502static symtab_t *pnpproto(pnpid_t *id);
503
504static symtab_t *gettoken(symtab_t *tab, char *s, int len);
505static char *gettokenname(symtab_t *tab, int val);
506
507static void mremote_serversetup();
508static void mremote_clientchg(int add);
509
918cebb3
MS
510static int kidspad(u_char rxc, mousestatus_t *act);
511
512static int usbmodule(void);
984263bc
MD
513
514int
918cebb3 515main(int argc, char *argv[])
984263bc
MD
516{
517 int c;
518 int i;
519 int j;
918cebb3 520 int retry;
984263bc
MD
521
522 for (i = 0; i < MOUSE_MAXBUTTON; ++i)
523 mstate[i] = &bstate[i];
524
918cebb3 525 while ((c = getopt(argc, argv, "3C:DE:F:I:PRS:VU:a:cdfhi:l:m:p:r:st:w:z:")) != -1)
984263bc
MD
526 switch(c) {
527
528 case '3':
529 rodent.flags |= Emulate3Button;
530 break;
531
532 case 'E':
533 rodent.button2timeout = atoi(optarg);
918cebb3
MS
534 if ((rodent.button2timeout < 0) ||
535 (rodent.button2timeout > MAX_BUTTON2TIMEOUT)) {
536 warnx("invalid argument `%s'", optarg);
537 usage();
984263bc
MD
538 }
539 break;
540
541 case 'a':
542 i = sscanf(optarg, "%f,%f", &rodent.accelx, &rodent.accely);
543 if (i == 0) {
544 warnx("invalid acceleration argument '%s'", optarg);
545 usage();
546 }
918cebb3 547
984263bc
MD
548 if (i == 1)
549 rodent.accely = rodent.accelx;
918cebb3 550
984263bc 551 break;
918cebb3 552
984263bc
MD
553 case 'c':
554 rodent.flags |= ChordMiddle;
555 break;
556
557 case 'd':
558 ++debug;
559 break;
560
561 case 'f':
562 nodaemon = TRUE;
563 break;
564
565 case 'i':
566 if (strcmp(optarg, "all") == 0)
918cebb3 567 identify = ID_ALL;
984263bc 568 else if (strcmp(optarg, "port") == 0)
918cebb3 569 identify = ID_PORT;
984263bc 570 else if (strcmp(optarg, "if") == 0)
918cebb3 571 identify = ID_IF;
984263bc 572 else if (strcmp(optarg, "type") == 0)
918cebb3 573 identify = ID_TYPE;
984263bc 574 else if (strcmp(optarg, "model") == 0)
918cebb3 575 identify = ID_MODEL;
984263bc 576 else {
918cebb3
MS
577 warnx("invalid argument `%s'", optarg);
578 usage();
984263bc
MD
579 }
580 nodaemon = TRUE;
581 break;
582
583 case 'l':
584 rodent.level = atoi(optarg);
585 if ((rodent.level < 0) || (rodent.level > 4)) {
918cebb3
MS
586 warnx("invalid argument `%s'", optarg);
587 usage();
984263bc
MD
588 }
589 break;
590
591 case 'm':
592 if (!r_installmap(optarg)) {
918cebb3
MS
593 warnx("invalid argument `%s'", optarg);
594 usage();
984263bc
MD
595 }
596 break;
597
598 case 'p':
599 rodent.portname = optarg;
600 break;
601
602 case 'r':
603 if (strcmp(optarg, "high") == 0)
918cebb3 604 rodent.resolution = MOUSE_RES_HIGH;
984263bc 605 else if (strcmp(optarg, "medium-high") == 0)
918cebb3 606 rodent.resolution = MOUSE_RES_HIGH;
984263bc 607 else if (strcmp(optarg, "medium-low") == 0)
918cebb3 608 rodent.resolution = MOUSE_RES_MEDIUMLOW;
984263bc 609 else if (strcmp(optarg, "low") == 0)
918cebb3 610 rodent.resolution = MOUSE_RES_LOW;
984263bc 611 else if (strcmp(optarg, "default") == 0)
918cebb3 612 rodent.resolution = MOUSE_RES_DEFAULT;
984263bc 613 else {
918cebb3
MS
614 rodent.resolution = atoi(optarg);
615 if (rodent.resolution <= 0) {
616 warnx("invalid argument `%s'", optarg);
617 usage();
618 }
984263bc
MD
619 }
620 break;
621
622 case 's':
623 rodent.baudrate = 9600;
624 break;
625
626 case 'w':
627 i = atoi(optarg);
628 if ((i <= 0) || (i > MOUSE_MAXBUTTON)) {
629 warnx("invalid argument `%s'", optarg);
630 usage();
631 }
632 rodent.wmode = 1 << (i - 1);
633 break;
634
635 case 'z':
636 if (strcmp(optarg, "x") == 0)
637 rodent.zmap[0] = MOUSE_XAXIS;
638 else if (strcmp(optarg, "y") == 0)
639 rodent.zmap[0] = MOUSE_YAXIS;
918cebb3 640 else {
984263bc 641 i = atoi(optarg);
918cebb3
MS
642 /*
643 * Use button i for negative Z axis movement and
984263bc
MD
644 * button (i + 1) for positive Z axis movement.
645 */
646 if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
918cebb3
MS
647 warnx("invalid argument `%s'", optarg);
648 usage();
984263bc
MD
649 }
650 rodent.zmap[0] = i;
651 rodent.zmap[1] = i + 1;
652 debug("optind: %d, optarg: '%s'", optind, optarg);
653 for (j = 1; j < 4; ++j) {
654 if ((optind >= argc) || !isdigit(*argv[optind]))
655 break;
656 i = atoi(argv[optind]);
657 if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
658 warnx("invalid argument `%s'", argv[optind]);
659 usage();
660 }
661 rodent.zmap[j] = i;
662 ++optind;
663 }
664 if ((rodent.zmap[2] != 0) && (rodent.zmap[3] == 0))
665 rodent.zmap[3] = rodent.zmap[2] + 1;
666 }
667 break;
668
669 case 'C':
670 rodent.clickthreshold = atoi(optarg);
918cebb3
MS
671 if ((rodent.clickthreshold < 0) ||
672 (rodent.clickthreshold > MAX_CLICKTHRESHOLD)) {
673 warnx("invalid argument `%s'", optarg);
674 usage();
984263bc
MD
675 }
676 break;
677
678 case 'D':
679 rodent.flags |= ClearDTR;
680 break;
681
682 case 'F':
683 rodent.rate = atoi(optarg);
684 if (rodent.rate <= 0) {
918cebb3
MS
685 warnx("invalid argument `%s'", optarg);
686 usage();
984263bc
MD
687 }
688 break;
689
690 case 'I':
691 pidfile = optarg;
692 break;
693
694 case 'P':
695 rodent.flags |= NoPnP;
696 break;
697
698 case 'R':
699 rodent.flags |= ClearRTS;
700 break;
701
702 case 'S':
703 rodent.baudrate = atoi(optarg);
704 if (rodent.baudrate <= 0) {
918cebb3
MS
705 warnx("invalid argument `%s'", optarg);
706 usage();
984263bc
MD
707 }
708 debug("rodent baudrate %d", rodent.baudrate);
709 break;
710
711 case 't':
712 if (strcmp(optarg, "auto") == 0) {
713 rodent.rtype = MOUSE_PROTO_UNKNOWN;
714 rodent.flags &= ~NoPnP;
715 rodent.level = -1;
716 break;
717 }
718 for (i = 0; rnames[i]; i++)
719 if (strcmp(optarg, rnames[i]) == 0) {
720 rodent.rtype = i;
721 rodent.flags |= NoPnP;
722 rodent.level = (i == MOUSE_PROTO_SYSMOUSE) ? 1 : 0;
723 break;
724 }
725 if (rnames[i])
726 break;
727 warnx("no such mouse type `%s'", optarg);
728 usage();
729
918cebb3
MS
730 case 'V':
731 rodent.flags |= VirtualScroll;
732 break;
733 case 'U':
734 rodent.scrollthreshold = atoi(optarg);
735 if (rodent.scrollthreshold < 0) {
736 warnx("invalid argument `%s'", optarg);
737 usage();
738 }
739 break;
740
984263bc
MD
741 case 'h':
742 case '?':
743 default:
744 usage();
745 }
746
747 /* fix Z axis mapping */
748 for (i = 0; i < 4; ++i) {
749 if (rodent.zmap[i] > 0) {
750 for (j = 0; j < MOUSE_MAXBUTTON; ++j) {
751 if (mstate[j] == &bstate[rodent.zmap[i] - 1])
752 mstate[j] = &zstate[i];
753 }
754 rodent.zmap[i] = 1 << (rodent.zmap[i] - 1);
755 }
756 }
757
758 /* the default port name */
759 switch(rodent.rtype) {
760
984263bc
MD
761 case MOUSE_PROTO_PS2:
762 if (!rodent.portname)
763 rodent.portname = "/dev/psm0";
764 break;
765
766 default:
767 if (rodent.portname)
768 break;
769 warnx("no port name specified");
770 usage();
771 }
772
918cebb3
MS
773 retry = 1;
774 if (strncmp(rodent.portname, "/dev/ums", 8) == 0) {
775 if (usbmodule() != 0)
776 retry = 5;
777 }
778
984263bc
MD
779 for (;;) {
780 if (setjmp(env) == 0) {
781 signal(SIGHUP, hup);
782 signal(SIGINT , cleanup);
783 signal(SIGQUIT, cleanup);
784 signal(SIGTERM, cleanup);
918cebb3
MS
785 for (i = 0; i < retry; ++i) {
786 if (i > 0)
787 sleep(2);
788 rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK);
789 if (rodent.mfd != -1 || errno != ENOENT)
790 break;
791 }
792 if (rodent.mfd == -1)
793 logerr(1, "unable to open %s", rodent.portname);
794 if (r_identify() == MOUSE_PROTO_UNKNOWN) {
795 logwarnx("cannot determine mouse type on %s", rodent.portname);
796 close(rodent.mfd);
797 rodent.mfd = -1;
798 }
984263bc
MD
799
800 /* print some information */
918cebb3 801 if (identify != ID_NONE) {
984263bc 802 if (identify == ID_ALL)
918cebb3
MS
803 printf("%s %s %s %s\n",
804 rodent.portname, r_if(rodent.hw.iftype),
805 r_name(rodent.rtype), r_model(rodent.hw.model));
984263bc
MD
806 else if (identify & ID_PORT)
807 printf("%s\n", rodent.portname);
808 else if (identify & ID_IF)
809 printf("%s\n", r_if(rodent.hw.iftype));
810 else if (identify & ID_TYPE)
811 printf("%s\n", r_name(rodent.rtype));
812 else if (identify & ID_MODEL)
813 printf("%s\n", r_model(rodent.hw.model));
814 exit(0);
815 } else {
918cebb3 816 debug("port: %s interface: %s type: %s model: %s",
984263bc
MD
817 rodent.portname, r_if(rodent.hw.iftype),
818 r_name(rodent.rtype), r_model(rodent.hw.model));
819 }
820
821 if (rodent.mfd == -1) {
918cebb3
MS
822 /*
823 * We cannot continue because of error. Exit if the
824 * program has not become a daemon. Otherwise, block
825 * until the the user corrects the problem and issues SIGHUP.
826 */
827 if (!background)
984263bc 828 exit(1);
918cebb3 829 sigpause(0);
984263bc
MD
830 }
831
918cebb3 832 r_init(); /* call init function */
984263bc
MD
833 moused();
834 }
835
836 if (rodent.mfd != -1)
837 close(rodent.mfd);
838 if (rodent.cfd != -1)
839 close(rodent.cfd);
840 rodent.mfd = rodent.cfd = -1;
841 }
842 /* NOT REACHED */
843
844 exit(0);
845}
846
918cebb3
MS
847static int
848usbmodule(void)
849{
850 struct kld_file_stat fstat;
851 struct module_stat mstat;
852 int fileid, modid;
853 int loaded;
854
855 for (loaded = 0, fileid = kldnext(0); !loaded && fileid > 0;
856 fileid = kldnext(fileid)) {
857 fstat.version = sizeof(fstat);
858 if (kldstat(fileid, &fstat) < 0)
859 continue;
860 if (strncmp(fstat.name, "uhub/ums", 8) == 0) {
861 loaded = 1;
862 break;
863 }
864 for (modid = kldfirstmod(fileid); modid > 0;
865 modid = modfnext(modid)) {
866 mstat.version = sizeof(mstat);
867 if (modstat(modid, &mstat) < 0)
868 continue;
869 if (strncmp(mstat.name, "uhub/ums", 8) == 0) {
870 loaded = 1;
871 break;
872 }
873 }
874 }
875 if (!loaded) {
876 if (kldload("ums") != -1)
877 return 1;
878 if (errno != EEXIST)
879 logerr(1, "unable to load USB mouse driver");
880 }
881 return 0;
882}
883
984263bc
MD
884static void
885moused(void)
886{
887 struct mouse_info mouse;
888 mousestatus_t action0; /* original mouse action */
889 mousestatus_t action; /* interrim buffer */
890 mousestatus_t action2; /* mapped action */
891 struct timeval timeout;
892 fd_set fds;
0ca6557d 893 u_char buf[16];
984263bc 894 FILE *fp;
0ca6557d
IV
895 int fill = 0;
896 int pos = 0;
984263bc
MD
897 int flags;
898 int c;
899 int i;
900
901 if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
918cebb3 902 logerr(1, "cannot open /dev/consolectl");
984263bc 903
918cebb3 904 if (!nodaemon && !background) {
984263bc 905 if (daemon(0, 0)) {
918cebb3 906 logerr(1, "failed to become a daemon");
984263bc
MD
907 } else {
908 background = TRUE;
909 fp = fopen(pidfile, "w");
910 if (fp != NULL) {
911 fprintf(fp, "%d\n", getpid());
912 fclose(fp);
913 }
914 }
918cebb3 915 }
984263bc
MD
916
917 /* clear mouse data */
918 bzero(&action0, sizeof(action0));
919 bzero(&action, sizeof(action));
920 bzero(&action2, sizeof(action2));
921 bzero(&mouse, sizeof(mouse));
922 mouse_button_state = S0;
923 gettimeofday(&mouse_button_state_tv, NULL);
924 mouse_move_delayed = 0;
925 for (i = 0; i < MOUSE_MAXBUTTON; ++i) {
926 bstate[i].count = 0;
927 bstate[i].tv = mouse_button_state_tv;
928 }
e62ef63c 929 for (i = 0; i < NELEM(zstate); ++i) {
984263bc
MD
930 zstate[i].count = 0;
931 zstate[i].tv = mouse_button_state_tv;
932 }
933
934 /* choose which ioctl command to use */
935 mouse.operation = MOUSE_MOTION_EVENT;
936 extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
937
938 /* process mouse data */
939 timeout.tv_sec = 0;
940 timeout.tv_usec = 20000; /* 20 msec */
941 for (;;) {
0ca6557d
IV
942 if (fill > pos)
943 goto more;
944 fill = 0;
984263bc
MD
945
946 FD_ZERO(&fds);
947 FD_SET(rodent.mfd, &fds);
948 if (rodent.mremsfd >= 0)
949 FD_SET(rodent.mremsfd, &fds);
950 if (rodent.mremcfd >= 0)
951 FD_SET(rodent.mremcfd, &fds);
952
953 c = select(FD_SETSIZE, &fds, NULL, NULL,
954 (rodent.flags & Emulate3Button) ? &timeout : NULL);
955 if (c < 0) { /* error */
918cebb3 956 logwarn("failed to read from mouse");
984263bc
MD
957 continue;
958 } else if (c == 0) { /* timeout */
959 /* assert(rodent.flags & Emulate3Button) */
960 action0.button = action0.obutton;
961 action0.dx = action0.dy = action0.dz = 0;
962 action0.flags = flags = 0;
963 if (r_timeout() && r_statetrans(&action0, &action, A_TIMEOUT)) {
964 if (debug > 2)
965 debug("flags:%08x buttons:%08x obuttons:%08x",
966 action.flags, action.button, action.obutton);
967 } else {
968 action0.obutton = action0.button;
969 continue;
970 }
971 } else {
972 /* MouseRemote client connect/disconnect */
973 if ((rodent.mremsfd >= 0) && FD_ISSET(rodent.mremsfd, &fds)) {
974 mremote_clientchg(TRUE);
975 continue;
976 }
977 if ((rodent.mremcfd >= 0) && FD_ISSET(rodent.mremcfd, &fds)) {
978 mremote_clientchg(FALSE);
979 continue;
980 }
981 /* mouse movement */
0ca6557d 982 if ((fill = read(rodent.mfd, buf, sizeof(buf))) == -1) {
984263bc
MD
983 if (errno == EWOULDBLOCK)
984 continue;
985 else
986 return;
987 }
0ca6557d
IV
988 pos = 0;
989more:
990 if ((flags = r_protocol(buf[pos++], &action0)) == 0)
984263bc 991 continue;
918cebb3
MS
992
993 if (rodent.flags & VirtualScroll) {
994 /* Allow middle button drags to scroll up and down */
995 if (action0.button == MOUSE_BUTTON2DOWN) {
996 if (scroll_state == SCROLL_NOTSCROLLING) {
997 scroll_state = SCROLL_PREPARE;
998 debug("PREPARING TO SCROLL");
999 }
1000 debug("[BUTTON2] flags:%08x buttons:%08x obuttons:%08x",
1001 action.flags, action.button, action.obutton);
1002 } else {
1003 debug("[NOTBUTTON2] flags:%08x buttons:%08x obuttons:%08x",
1004 action.flags, action.button, action.obutton);
1005
1006 /* This isn't a middle button down... move along... */
1007 if (scroll_state == SCROLL_SCROLLING) {
1008 /*
1009 * We were scrolling, someone let go of button 2.
1010 * Now turn autoscroll off.
1011 */
1012 scroll_state = SCROLL_NOTSCROLLING;
1013 debug("DONE WITH SCROLLING / %d", scroll_state);
1014 } else if (scroll_state == SCROLL_PREPARE) {
1015 mousestatus_t newaction = action0;
1016
1017 /* We were preparing to scroll, but we never moved... */
1018 r_timestamp(&action0);
1019 r_statetrans(&action0, &newaction,
1020 A(newaction.button & MOUSE_BUTTON1DOWN,
1021 action0.button & MOUSE_BUTTON3DOWN));
1022
1023 /* Send middle down */
1024 newaction.button = MOUSE_BUTTON2DOWN;
1025 r_click(&newaction);
1026
1027 /* Send middle up */
1028 r_timestamp(&newaction);
1029 newaction.obutton = newaction.button;
1030 newaction.button = action0.button;
1031 r_click(&newaction);
1032 }
1033 }
1034 }
1035
984263bc 1036 r_timestamp(&action0);
918cebb3
MS
1037 r_statetrans(&action0, &action,
1038 A(action0.button & MOUSE_BUTTON1DOWN,
1039 action0.button & MOUSE_BUTTON3DOWN));
984263bc
MD
1040 debug("flags:%08x buttons:%08x obuttons:%08x", action.flags,
1041 action.button, action.obutton);
1042 }
1043 action0.obutton = action0.button;
1044 flags &= MOUSE_POSCHANGED;
1045 flags |= action.obutton ^ action.button;
1046 action.flags = flags;
1047
1048 if (flags) { /* handler detected action */
1049 r_map(&action, &action2);
1050 debug("activity : buttons 0x%08x dx %d dy %d dz %d",
1051 action2.button, action2.dx, action2.dy, action2.dz);
1052
918cebb3
MS
1053 if (rodent.flags & VirtualScroll) {
1054 /*
1055 * If *only* the middle button is pressed AND we are moving
1056 * the stick/trackpoint/nipple, scroll!
1057 */
1058 if (scroll_state == SCROLL_PREPARE) {
1059 /* Ok, Set we're really scrolling now.... */
1060 if (action2.dy || action2.dx)
1061 scroll_state = SCROLL_SCROLLING;
1062 }
1063 if (scroll_state == SCROLL_SCROLLING) {
1064 scroll_movement += action2.dy;
1065 debug("SCROLL: %d", scroll_movement);
1066
1067 if (scroll_movement < -rodent.scrollthreshold) {
1068 /* Scroll down */
1069 action2.dz = -1;
1070 scroll_movement = 0;
1071 }
1072 else if (scroll_movement > rodent.scrollthreshold) {
1073 /* Scroll up */
1074 action2.dz = 1;
1075 scroll_movement = 0;
1076 }
1077
1078 /* Don't move while scrolling */
1079 action2.dx = action2.dy = 0;
1080 }
1081 }
1082
984263bc 1083 if (extioctl) {
918cebb3
MS
1084 /* Defer clicks until we aren't VirtualScroll'ing. */
1085 if (scroll_state == SCROLL_NOTSCROLLING)
1086 r_click(&action2);
1087
1088 if (action2.flags & MOUSE_POSCHANGED) {
1089 mouse.operation = MOUSE_MOTION_EVENT;
1090 mouse.u.data.buttons = action2.button;
1091 mouse.u.data.x = action2.dx * rodent.accelx;
1092 mouse.u.data.y = action2.dy * rodent.accely;
1093 mouse.u.data.z = action2.dz;
984263bc 1094 if (debug < 2)
918cebb3
MS
1095 ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
1096 }
984263bc 1097 } else {
918cebb3
MS
1098 mouse.operation = MOUSE_ACTION;
1099 mouse.u.data.buttons = action2.button;
1100 mouse.u.data.x = action2.dx * rodent.accelx;
1101 mouse.u.data.y = action2.dy * rodent.accely;
1102 mouse.u.data.z = action2.dz;
984263bc 1103 if (debug < 2)
918cebb3 1104 ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
984263bc
MD
1105 }
1106
918cebb3
MS
1107 /*
1108 * If the Z axis movement is mapped to an imaginary physical
984263bc
MD
1109 * button, we need to cook up a corresponding button `up' event
1110 * after sending a button `down' event.
1111 */
918cebb3 1112 if ((rodent.zmap[0] > 0) && (action.dz != 0)) {
984263bc
MD
1113 action.obutton = action.button;
1114 action.dx = action.dy = action.dz = 0;
918cebb3
MS
1115 r_map(&action, &action2);
1116 debug("activity : buttons 0x%08x dx %d dy %d dz %d",
984263bc
MD
1117 action2.button, action2.dx, action2.dy, action2.dz);
1118
918cebb3
MS
1119 if (extioctl) {
1120 r_click(&action2);
1121 } else {
1122 mouse.operation = MOUSE_ACTION;
1123 mouse.u.data.buttons = action2.button;
984263bc
MD
1124 mouse.u.data.x = mouse.u.data.y = mouse.u.data.z = 0;
1125 if (debug < 2)
918cebb3
MS
1126 ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
1127 }
984263bc
MD
1128 }
1129 }
1130 }
918cebb3 1131 /* NOT REACHED */
25389281 1132}
984263bc
MD
1133
1134static void
1135hup(int sig)
1136{
1137 longjmp(env, 1);
1138}
1139
918cebb3 1140static void
984263bc
MD
1141cleanup(int sig)
1142{
1143 if (rodent.rtype == MOUSE_PROTO_X10MOUSEREM)
1144 unlink(_PATH_MOUSEREMOTE);
1145 exit(0);
1146}
1147
1148/**
1149 ** usage
1150 **
1151 ** Complain, and free the CPU for more worthy tasks
1152 **/
1153static void
1154usage(void)
1155{
1156 fprintf(stderr, "%s\n%s\n%s\n%s\n",
1157 "usage: moused [-DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
918cebb3
MS
1158 " [-V [-U threshold]] [-a X [,Y]] [-C threshold] [-m N=M] [-w N]",
1159 " [-z N] [-t <mousetype>] [-l level] [-3 [-E timeout]] -p <port>",
984263bc
MD
1160 " moused [-d] -i <port|if|type|model|all> -p <port>");
1161 exit(1);
1162}
1163
918cebb3
MS
1164/*
1165 * Output an error message to syslog or stderr as appropriate. If
1166 * `errnum' is non-zero, append its string form to the message.
1167 */
1168static void
1169log_or_warn(int log_pri, int errnum, const char *fmt, ...)
1170{
1171 va_list ap;
1172 char buf[256];
1173
1174 va_start(ap, fmt);
1175 vsnprintf(buf, sizeof(buf), fmt, ap);
1176 va_end(ap);
1177 if (errnum) {
1178 strlcat(buf, ": ", sizeof(buf));
1179 strlcat(buf, strerror(errnum), sizeof(buf));
1180 }
1181
1182 if (background)
1183 syslog(log_pri, "%s", buf);
1184 else
1185 warnx("%s", buf);
1186}
1187
984263bc
MD
1188/**
1189 ** Mouse interface code, courtesy of XFree86 3.1.2.
1190 **
1191 ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
1192 ** to clean, reformat and rationalise naming, it's quite possible that
1193 ** some things in here have been broken.
1194 **
1195 ** I hope not 8)
1196 **
1197 ** The following code is derived from a module marked :
1198 **/
1199
1200/* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
1201/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
1202 17:03:40 dawes Exp $ */
1203/*
1204 *
1205 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
1206 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
1207 *
1208 * Permission to use, copy, modify, distribute, and sell this software and its
1209 * documentation for any purpose is hereby granted without fee, provided that
1210 * the above copyright notice appear in all copies and that both that
1211 * copyright notice and this permission notice appear in supporting
1212 * documentation, and that the names of Thomas Roell and David Dawes not be
1213 * used in advertising or publicity pertaining to distribution of the
1214 * software without specific, written prior permission. Thomas Roell
1215 * and David Dawes makes no representations about the suitability of this
1216 * software for any purpose. It is provided "as is" without express or
1217 * implied warranty.
1218 *
1219 * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
1220 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
1221 * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
1222 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
1223 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
1224 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1225 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1226 *
1227 */
1228
1229/**
1230 ** GlidePoint support from XFree86 3.2.
1231 ** Derived from the module:
1232 **/
1233
1234/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.19 1996/10/16 14:40:51 dawes Exp $ */
1235/* $XConsortium: xf86_Mouse.c /main/10 1996/01/30 15:16:12 kaleb $ */
1236
1237/* the following table must be ordered by MOUSE_PROTO_XXX in mouse.h */
1238static unsigned char proto[][7] = {
1239 /* hd_mask hd_id dp_mask dp_id bytes b4_mask b4_id */
918cebb3 1240 { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* MicroSoft */
984263bc
MD
1241 { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* MouseSystems */
1242 { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* Logitech */
1243 { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MMSeries */
918cebb3 1244 { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* MouseMan */
984263bc
MD
1245 { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* Bus */
1246 { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* InPort */
1247 { 0xc0, 0x00, 0x00, 0x00, 3, 0x00, 0xff }, /* PS/2 mouse */
1248 { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MM HitTablet */
918cebb3
MS
1249 { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* GlidePoint */
1250 { 0x40, 0x40, 0x40, 0x00, 3, ~0x3f, 0x00 }, /* IntelliMouse */
1251 { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* ThinkingMouse */
984263bc 1252 { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* sysmouse */
918cebb3 1253 { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* X10 MouseRem */
984263bc
MD
1254 { 0x80, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* KIDSPAD */
1255 { 0xc3, 0xc0, 0x00, 0x00, 6, 0x00, 0xff }, /* VersaPad */
1256 { 0x00, 0x00, 0x00, 0x00, 1, 0x00, 0xff }, /* JogDial */
2cc577c1 1257#if 0 /* not yet */
984263bc
MD
1258 { 0xf8, 0x80, 0x00, 0x00, 5, ~0x2f, 0x10 }, /* Mariqua */
1259#endif
1260};
1261static unsigned char cur_proto[7];
1262
1263static int
1264r_identify(void)
1265{
1266 char pnpbuf[256]; /* PnP identifier string may be up to 256 bytes long */
1267 pnpid_t pnpid;
1268 symtab_t *t;
1269 int level;
1270 int len;
1271
1272 /* set the driver operation level, if applicable */
1273 if (rodent.level < 0)
1274 rodent.level = 1;
1275 ioctl(rodent.mfd, MOUSE_SETLEVEL, &rodent.level);
1276 rodent.level = (ioctl(rodent.mfd, MOUSE_GETLEVEL, &level) == 0) ? level : 0;
1277
1278 /*
918cebb3 1279 * Interrogate the driver and get some intelligence on the device...
984263bc
MD
1280 * The following ioctl functions are not always supported by device
1281 * drivers. When the driver doesn't support them, we just trust the
1282 * user to supply valid information.
1283 */
1284 rodent.hw.iftype = MOUSE_IF_UNKNOWN;
1285 rodent.hw.model = MOUSE_MODEL_GENERIC;
1286 ioctl(rodent.mfd, MOUSE_GETHWINFO, &rodent.hw);
1287
1288 if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
918cebb3 1289 bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
984263bc
MD
1290 rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
1291 rodent.mode.rate = -1;
1292 rodent.mode.resolution = MOUSE_RES_UNKNOWN;
1293 rodent.mode.accelfactor = 0;
1294 rodent.mode.level = 0;
1295 if (ioctl(rodent.mfd, MOUSE_GETMODE, &rodent.mode) == 0) {
918cebb3 1296 if ((rodent.mode.protocol == MOUSE_PROTO_UNKNOWN)
e62ef63c 1297 || (rodent.mode.protocol >= NELEM(proto))) {
984263bc
MD
1298 logwarnx("unknown mouse protocol (%d)", rodent.mode.protocol);
1299 return MOUSE_PROTO_UNKNOWN;
918cebb3 1300 } else {
984263bc
MD
1301 /* INPORT and BUS are the same... */
1302 if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
918cebb3 1303 rodent.mode.protocol = MOUSE_PROTO_BUS;
984263bc
MD
1304 if (rodent.mode.protocol != rodent.rtype) {
1305 /* Hmm, the driver doesn't agree with the user... */
918cebb3
MS
1306 if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1307 logwarnx("mouse type mismatch (%s != %s), %s is assumed",
1308 r_name(rodent.mode.protocol), r_name(rodent.rtype),
1309 r_name(rodent.mode.protocol));
1310 rodent.rtype = rodent.mode.protocol;
1311 bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
984263bc 1312 }
918cebb3
MS
1313 }
1314 cur_proto[4] = rodent.mode.packetsize;
1315 cur_proto[0] = rodent.mode.syncmask[0]; /* header byte bit mask */
1316 cur_proto[1] = rodent.mode.syncmask[1]; /* header bit pattern */
984263bc
MD
1317 }
1318
918cebb3 1319 /* maybe this is a PnP mouse... */
984263bc
MD
1320 if (rodent.mode.protocol == MOUSE_PROTO_UNKNOWN) {
1321
918cebb3
MS
1322 if (rodent.flags & NoPnP)
1323 return rodent.rtype;
984263bc 1324 if (((len = pnpgets(pnpbuf)) <= 0) || !pnpparse(&pnpid, pnpbuf, len))
918cebb3 1325 return rodent.rtype;
984263bc 1326
918cebb3
MS
1327 debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
1328 pnpid.neisaid, pnpid.neisaid, pnpid.eisaid,
1329 pnpid.ncompat, pnpid.ncompat, pnpid.compat,
984263bc
MD
1330 pnpid.ndescription, pnpid.ndescription, pnpid.description);
1331
1332 /* we have a valid PnP serial device ID */
918cebb3 1333 rodent.hw.iftype = MOUSE_IF_SERIAL;
984263bc
MD
1334 t = pnpproto(&pnpid);
1335 if (t != NULL) {
918cebb3
MS
1336 rodent.mode.protocol = t->val;
1337 rodent.hw.model = t->val2;
984263bc 1338 } else {
918cebb3 1339 rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
984263bc
MD
1340 }
1341 if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
1342 rodent.mode.protocol = MOUSE_PROTO_BUS;
1343
918cebb3 1344 /* make final adjustment */
984263bc
MD
1345 if (rodent.mode.protocol != MOUSE_PROTO_UNKNOWN) {
1346 if (rodent.mode.protocol != rodent.rtype) {
1347 /* Hmm, the device doesn't agree with the user... */
918cebb3
MS
1348 if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
1349 logwarnx("mouse type mismatch (%s != %s), %s is assumed",
1350 r_name(rodent.mode.protocol), r_name(rodent.rtype),
1351 r_name(rodent.mode.protocol));
1352 rodent.rtype = rodent.mode.protocol;
1353 bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
984263bc
MD
1354 }
1355 }
1356 }
1357
1358 debug("proto params: %02x %02x %02x %02x %d %02x %02x",
918cebb3 1359 cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
984263bc
MD
1360 cur_proto[4], cur_proto[5], cur_proto[6]);
1361
1362 return rodent.rtype;
1363}
1364
1365static char *
1366r_if(int iftype)
1367{
1368 char *s;
1369
1370 s = gettokenname(rifs, iftype);
1371 return (s == NULL) ? "unknown" : s;
1372}
1373
1374static char *
1375r_name(int type)
1376{
918cebb3 1377 return ((type == MOUSE_PROTO_UNKNOWN)
e62ef63c 1378 || (type > NELEM(rnames) - 1))
984263bc
MD
1379 ? "unknown" : rnames[type];
1380}
1381
1382static char *
1383r_model(int model)
1384{
1385 char *s;
1386
1387 s = gettokenname(rmodels, model);
1388 return (s == NULL) ? "unknown" : s;
1389}
1390
1391static void
1392r_init(void)
1393{
1394 unsigned char buf[16]; /* scrach buffer */
1395 fd_set fds;
1396 char *s;
1397 char c;
1398 int i;
1399
1400 /**
918cebb3 1401 ** This comment is a little out of context here, but it contains
984263bc
MD
1402 ** some useful information...
1403 ********************************************************************
1404 **
1405 ** The following lines take care of the Logitech MouseMan protocols.
1406 **
1407 ** NOTE: There are different versions of both MouseMan and TrackMan!
1408 ** Hence I add another protocol P_LOGIMAN, which the user can
1409 ** specify as MouseMan in his XF86Config file. This entry was
1410 ** formerly handled as a special case of P_MS. However, people
1411 ** who don't have the middle button problem, can still specify
1412 ** Microsoft and use P_MS.
1413 **
1414 ** By default, these mice should use a 3 byte Microsoft protocol
1415 ** plus a 4th byte for the middle button. However, the mouse might
1416 ** have switched to a different protocol before we use it, so I send
1417 ** the proper sequence just in case.
1418 **
1419 ** NOTE: - all commands to (at least the European) MouseMan have to
1420 ** be sent at 1200 Baud.
1421 ** - each command starts with a '*'.
1422 ** - whenever the MouseMan receives a '*', it will switch back
1423 ** to 1200 Baud. Hence I have to select the desired protocol
1424 ** first, then select the baud rate.
1425 **
1426 ** The protocols supported by the (European) MouseMan are:
1427 ** - 5 byte packed binary protocol, as with the Mouse Systems
1428 ** mouse. Selected by sequence "*U".
1429 ** - 2 button 3 byte MicroSoft compatible protocol. Selected
1430 ** by sequence "*V".
1431 ** - 3 button 3+1 byte MicroSoft compatible protocol (default).
1432 ** Selected by sequence "*X".
1433 **
1434 ** The following baud rates are supported:
1435 ** - 1200 Baud (default). Selected by sequence "*n".
1436 ** - 9600 Baud. Selected by sequence "*q".
1437 **
1438 ** Selecting a sample rate is no longer supported with the MouseMan!
1439 ** Some additional lines in xf86Config.c take care of ill configured
1440 ** baud rates and sample rates. (The user will get an error.)
1441 */
1442
1443 switch (rodent.rtype) {
1444
1445 case MOUSE_PROTO_LOGI:
918cebb3 1446 /*
984263bc 1447 * The baud rate selection command must be sent at the current
918cebb3 1448 * baud rate; try all likely settings
984263bc
MD
1449 */
1450 setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
1451 setmousespeed(4800, rodent.baudrate, rodentcflags[rodent.rtype]);
1452 setmousespeed(2400, rodent.baudrate, rodentcflags[rodent.rtype]);
1453 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1454 /* select MM series data format */
1455 write(rodent.mfd, "S", 1);
1456 setmousespeed(rodent.baudrate, rodent.baudrate,
1457 rodentcflags[MOUSE_PROTO_MM]);
1458 /* select report rate/frequency */
1459 if (rodent.rate <= 0) write(rodent.mfd, "O", 1);
1460 else if (rodent.rate <= 15) write(rodent.mfd, "J", 1);
1461 else if (rodent.rate <= 27) write(rodent.mfd, "K", 1);
1462 else if (rodent.rate <= 42) write(rodent.mfd, "L", 1);
1463 else if (rodent.rate <= 60) write(rodent.mfd, "R", 1);
1464 else if (rodent.rate <= 85) write(rodent.mfd, "M", 1);
1465 else if (rodent.rate <= 125) write(rodent.mfd, "Q", 1);
1466 else write(rodent.mfd, "N", 1);
1467 break;
1468
1469 case MOUSE_PROTO_LOGIMOUSEMAN:
1470 /* The command must always be sent at 1200 baud */
1471 setmousespeed(1200, 1200, rodentcflags[rodent.rtype]);
1472 write(rodent.mfd, "*X", 2);
1473 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1474 break;
1475
1476 case MOUSE_PROTO_HITTAB:
1477 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1478
1479 /*
1480 * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
1481 * The tablet must be configured to be in MM mode, NO parity,
1482 * Binary Format. xf86Info.sampleRate controls the sensativity
1483 * of the tablet. We only use this tablet for it's 4-button puck
1484 * so we don't run in "Absolute Mode"
1485 */
1486 write(rodent.mfd, "z8", 2); /* Set Parity = "NONE" */
1487 usleep(50000);
1488 write(rodent.mfd, "zb", 2); /* Set Format = "Binary" */
1489 usleep(50000);
1490 write(rodent.mfd, "@", 1); /* Set Report Mode = "Stream" */
1491 usleep(50000);
1492 write(rodent.mfd, "R", 1); /* Set Output Rate = "45 rps" */
1493 usleep(50000);
1494 write(rodent.mfd, "I\x20", 2); /* Set Incrememtal Mode "20" */
1495 usleep(50000);
1496 write(rodent.mfd, "E", 1); /* Set Data Type = "Relative */
1497 usleep(50000);
1498
1499 /* Resolution is in 'lines per inch' on the Hitachi tablet */
918cebb3 1500 if (rodent.resolution == MOUSE_RES_LOW) c = 'g';
984263bc
MD
1501 else if (rodent.resolution == MOUSE_RES_MEDIUMLOW) c = 'e';
1502 else if (rodent.resolution == MOUSE_RES_MEDIUMHIGH) c = 'h';
1503 else if (rodent.resolution == MOUSE_RES_HIGH) c = 'd';
918cebb3
MS
1504 else if (rodent.resolution <= 40) c = 'g';
1505 else if (rodent.resolution <= 100) c = 'd';
1506 else if (rodent.resolution <= 200) c = 'e';
1507 else if (rodent.resolution <= 500) c = 'h';
1508 else if (rodent.resolution <= 1000) c = 'j';
1509 else c = 'd';
984263bc
MD
1510 write(rodent.mfd, &c, 1);
1511 usleep(50000);
1512
1513 write(rodent.mfd, "\021", 1); /* Resume DATA output */
1514 break;
1515
1516 case MOUSE_PROTO_THINK:
1517 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1518 /* the PnP ID string may be sent again, discard it */
1519 usleep(200000);
1520 i = FREAD;
1521 ioctl(rodent.mfd, TIOCFLUSH, &i);
1522 /* send the command to initialize the beast */
1523 for (s = "E5E5"; *s; ++s) {
1524 write(rodent.mfd, s, 1);
1525 FD_ZERO(&fds);
1526 FD_SET(rodent.mfd, &fds);
1527 if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1528 break;
1529 read(rodent.mfd, &c, 1);
1530 debug("%c", c);
1531 if (c != *s)
918cebb3 1532 break;
984263bc
MD
1533 }
1534 break;
1535
1536 case MOUSE_PROTO_JOGDIAL:
1537 break;
1538 case MOUSE_PROTO_MSC:
1539 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1540 if (rodent.flags & ClearDTR) {
1541 i = TIOCM_DTR;
1542 ioctl(rodent.mfd, TIOCMBIC, &i);
918cebb3
MS
1543 }
1544 if (rodent.flags & ClearRTS) {
984263bc
MD
1545 i = TIOCM_RTS;
1546 ioctl(rodent.mfd, TIOCMBIC, &i);
918cebb3 1547 }
984263bc
MD
1548 break;
1549
1550 case MOUSE_PROTO_SYSMOUSE:
1551 if (rodent.hw.iftype == MOUSE_IF_SYSMOUSE)
1552 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
918cebb3 1553 /* FALLTHROUGH */
984263bc
MD
1554
1555 case MOUSE_PROTO_BUS:
1556 case MOUSE_PROTO_INPORT:
1557 case MOUSE_PROTO_PS2:
1558 if (rodent.rate >= 0)
1559 rodent.mode.rate = rodent.rate;
1560 if (rodent.resolution != MOUSE_RES_UNKNOWN)
1561 rodent.mode.resolution = rodent.resolution;
1562 ioctl(rodent.mfd, MOUSE_SETMODE, &rodent.mode);
1563 break;
1564
1565 case MOUSE_PROTO_X10MOUSEREM:
1566 mremote_serversetup();
1567 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1568 break;
1569
1570
1571 case MOUSE_PROTO_VERSAPAD:
1572 tcsendbreak(rodent.mfd, 0); /* send break for 400 msec */
1573 i = FREAD;
1574 ioctl(rodent.mfd, TIOCFLUSH, &i);
1575 for (i = 0; i < 7; ++i) {
1576 FD_ZERO(&fds);
1577 FD_SET(rodent.mfd, &fds);
1578 if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1579 break;
1580 read(rodent.mfd, &c, 1);
1581 buf[i] = c;
1582 }
1583 debug("%s\n", buf);
1584 if ((buf[0] != 'V') || (buf[1] != 'P')|| (buf[7] != '\r'))
1585 break;
1586 setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
1587 tcsendbreak(rodent.mfd, 0); /* send break for 400 msec again */
1588 for (i = 0; i < 7; ++i) {
1589 FD_ZERO(&fds);
1590 FD_SET(rodent.mfd, &fds);
1591 if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
1592 break;
1593 read(rodent.mfd, &c, 1);
1594 debug("%c", c);
1595 if (c != buf[i])
1596 break;
1597 }
1598 i = FREAD;
1599 ioctl(rodent.mfd, TIOCFLUSH, &i);
1600 break;
1601
1602 default:
1603 setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
1604 break;
1605 }
1606}
1607
1608static int
1609r_protocol(u_char rBuf, mousestatus_t *act)
1610{
1611 /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
918cebb3 1612 static int butmapmss[4] = { /* Microsoft, MouseMan, GlidePoint,
984263bc 1613 IntelliMouse, Thinking Mouse */
918cebb3
MS
1614 0,
1615 MOUSE_BUTTON3DOWN,
1616 MOUSE_BUTTON1DOWN,
1617 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
984263bc 1618 };
918cebb3 1619 static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
984263bc 1620 Thinking Mouse */
918cebb3
MS
1621 0,
1622 MOUSE_BUTTON4DOWN,
1623 MOUSE_BUTTON2DOWN,
1624 MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
984263bc
MD
1625 };
1626 /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1627 static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
1628 MouseMan+ */
918cebb3
MS
1629 0,
1630 MOUSE_BUTTON2DOWN,
1631 MOUSE_BUTTON4DOWN,
1632 MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
984263bc
MD
1633 };
1634 /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
918cebb3 1635 static int butmapmsc[8] = { /* MouseSystems, MMSeries, Logitech,
984263bc 1636 Bus, sysmouse */
918cebb3
MS
1637 0,
1638 MOUSE_BUTTON3DOWN,
1639 MOUSE_BUTTON2DOWN,
1640 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1641 MOUSE_BUTTON1DOWN,
1642 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
984263bc
MD
1643 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
1644 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1645 };
1646 /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
1647 static int butmapps2[8] = { /* PS/2 */
918cebb3
MS
1648 0,
1649 MOUSE_BUTTON1DOWN,
1650 MOUSE_BUTTON3DOWN,
1651 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1652 MOUSE_BUTTON2DOWN,
1653 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
984263bc
MD
1654 MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
1655 MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
1656 };
1657 /* for Hitachi tablet */
1658 static int butmaphit[8] = { /* MM HitTablet */
918cebb3
MS
1659 0,
1660 MOUSE_BUTTON3DOWN,
1661 MOUSE_BUTTON2DOWN,
1662 MOUSE_BUTTON1DOWN,
1663 MOUSE_BUTTON4DOWN,
1664 MOUSE_BUTTON5DOWN,
1665 MOUSE_BUTTON6DOWN,
1666 MOUSE_BUTTON7DOWN,
984263bc
MD
1667 };
1668 /* for serial VersaPad */
1669 static int butmapversa[8] = { /* VersaPad */
918cebb3
MS
1670 0,
1671 0,
1672 MOUSE_BUTTON3DOWN,
1673 MOUSE_BUTTON3DOWN,
1674 MOUSE_BUTTON1DOWN,
1675 MOUSE_BUTTON1DOWN,
1676 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1677 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
984263bc
MD
1678 };
1679 /* for PS/2 VersaPad */
1680 static int butmapversaps2[8] = { /* VersaPad */
918cebb3
MS
1681 0,
1682 MOUSE_BUTTON3DOWN,
1683 0,
1684 MOUSE_BUTTON3DOWN,
1685 MOUSE_BUTTON1DOWN,
1686 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
1687 MOUSE_BUTTON1DOWN,
1688 MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
984263bc
MD
1689 };
1690 static int pBufP = 0;
1691 static unsigned char pBuf[8];
1692 static int prev_x, prev_y;
1693 static int on = FALSE;
1694 int x, y;
1695
1696 debug("received char 0x%x",(int)rBuf);
1697 if (rodent.rtype == MOUSE_PROTO_KIDSPAD)
1698 return kidspad(rBuf, act) ;
1699
1700 /*
1701 * Hack for resyncing: We check here for a package that is:
1702 * a) illegal (detected by wrong data-package header)
1703 * b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
1704 * c) bad header-package
1705 *
1706 * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
1707 * -128 are allowed, but since they are very seldom we can easily
1708 * use them as package-header with no button pressed.
1709 * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
1710 * 0x80 is not valid as a header byte. For a PS/2 mouse we skip
1711 * checking data bytes.
1712 * For resyncing a PS/2 mouse we require the two most significant
1713 * bits in the header byte to be 0. These are the overflow bits,
1714 * and in case of an overflow we actually lose sync. Overflows
1715 * are very rare, however, and we quickly gain sync again after
1716 * an overflow condition. This is the best we can do. (Actually,
1717 * we could use bit 0x08 in the header byte for resyncing, since
1718 * that bit is supposed to be always on, but nobody told
1719 * Microsoft...)
1720 */
1721
1722 if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
1723 ((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
1724 {
1725 pBufP = 0; /* skip package */
1726 }
918cebb3 1727
984263bc
MD
1728 if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
1729 return 0;
1730
1731 /* is there an extra data byte? */
1732 if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
1733 {
1734 /*
1735 * Hack for Logitech MouseMan Mouse - Middle button
1736 *
1737 * Unfortunately this mouse has variable length packets: the standard
1738 * Microsoft 3 byte packet plus an optional 4th byte whenever the
1739 * middle button status changes.
1740 *
1741 * We have already processed the standard packet with the movement
1742 * and button info. Now post an event message with the old status
1743 * of the left and right buttons and the updated middle button.
1744 */
1745
1746 /*
1747 * Even worse, different MouseMen and TrackMen differ in the 4th
1748 * byte: some will send 0x00/0x20, others 0x01/0x21, or even
1749 * 0x02/0x22, so I have to strip off the lower bits.
918cebb3
MS
1750 *
1751 * [JCH-96/01/21]
1752 * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
1753 * and it is activated by tapping the glidepad with the finger! 8^)
1754 * We map it to bit bit3, and the reverse map in xf86Events just has
1755 * to be extended so that it is identified as Button 4. The lower
1756 * half of the reverse-map may remain unchanged.
984263bc
MD
1757 */
1758
918cebb3 1759 /*
984263bc
MD
1760 * [KY-97/08/03]
1761 * Receive the fourth byte only when preceding three bytes have
1762 * been detected (pBufP >= cur_proto[4]). In the previous
1763 * versions, the test was pBufP == 0; thus, we may have mistakingly
918cebb3 1764 * received a byte even if we didn't see anything preceding
984263bc
MD
1765 * the byte.
1766 */
1767
1768 if ((rBuf & cur_proto[5]) != cur_proto[6]) {
918cebb3 1769 pBufP = 0;
984263bc
MD
1770 return 0;
1771 }
1772
1773 switch (rodent.rtype) {
2cc577c1 1774#if 0 /* not yet */
984263bc 1775 case MOUSE_PROTO_MARIQUA:
918cebb3 1776 /*
984263bc
MD
1777 * This mouse has 16! buttons in addition to the standard
1778 * three of them. They return 0x10 though 0x1f in the
1779 * so-called `ten key' mode and 0x30 though 0x3f in the
918cebb3 1780 * `function key' mode. As there are only 31 bits for
984263bc
MD
1781 * button state (including the standard three), we ignore
1782 * the bit 0x20 and don't distinguish the two modes.
1783 */
1784 act->dx = act->dy = act->dz = 0;
1785 act->obutton = act->button;
1786 rBuf &= 0x1f;
1787 act->button = (1 << (rBuf - 13))
918cebb3
MS
1788 | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1789 /*
1790 * FIXME: this is a button "down" event. There needs to be
984263bc
MD
1791 * a corresponding button "up" event... XXX
1792 */
1793 break;
1794#endif /* notyet */
1795 case MOUSE_PROTO_JOGDIAL:
1796 break;
1797
1798 /*
1799 * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
1800 * always send the fourth byte, whereas the fourth byte is
918cebb3
MS
1801 * optional for GlidePoint and ThinkingMouse. The fourth byte
1802 * is also optional for MouseMan+ and FirstMouse+ in their
1803 * native mode. It is always sent if they are in the IntelliMouse
984263bc 1804 * compatible mode.
918cebb3 1805 */
984263bc
MD
1806 case MOUSE_PROTO_INTELLI: /* IntelliMouse, NetMouse, Mie Mouse,
1807 MouseMan+ */
1808 act->dx = act->dy = 0;
1809 act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
1810 if ((act->dz >= 7) || (act->dz <= -7))
1811 act->dz = 0;
1812 act->obutton = act->button;
1813 act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1814 | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1815 break;
1816
1817 default:
1818 act->dx = act->dy = act->dz = 0;
1819 act->obutton = act->button;
1820 act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
1821 | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
1822 break;
1823 }
1824
1825 act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
1826 | (act->obutton ^ act->button);
918cebb3 1827 pBufP = 0;
984263bc
MD
1828 return act->flags;
1829 }
918cebb3 1830
984263bc
MD
1831 if (pBufP >= cur_proto[4])
1832 pBufP = 0;
1833 pBuf[pBufP++] = rBuf;
1834 if (pBufP != cur_proto[4])
1835 return 0;
918cebb3 1836
984263bc
MD
1837 /*
1838 * assembly full package
1839 */
1840
1841 debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
918cebb3
MS
1842 cur_proto[4],
1843 pBuf[0], pBuf[1], pBuf[2], pBuf[3],
984263bc
MD
1844 pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
1845
1846 act->dz = 0;
1847 act->obutton = act->button;
918cebb3 1848 switch (rodent.rtype)
984263bc
MD
1849 {
1850 case MOUSE_PROTO_MS: /* Microsoft */
1851 case MOUSE_PROTO_LOGIMOUSEMAN: /* MouseMan/TrackMan */
1852 case MOUSE_PROTO_X10MOUSEREM: /* X10 MouseRemote */
1853 act->button = act->obutton & MOUSE_BUTTON4DOWN;
1854 if (rodent.flags & ChordMiddle)
1855 act->button |= ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
918cebb3 1856 ? MOUSE_BUTTON2DOWN
984263bc
MD
1857 : butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1858 else
1859 act->button |= (act->obutton & MOUSE_BUTTON2DOWN)
1860 | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
918cebb3 1861
984263bc 1862 /* Send X10 btn events to remote client (ensure -128-+127 range) */
918cebb3 1863 if ((rodent.rtype == MOUSE_PROTO_X10MOUSEREM) &&
984263bc
MD
1864 ((pBuf[0] & 0xFC) == 0x44) && (pBuf[2] == 0x3F)) {
1865 if (rodent.mremcfd >= 0) {
918cebb3 1866 unsigned char key = (signed char)(((pBuf[0] & 0x03) << 6) |
984263bc 1867 (pBuf[1] & 0x3F));
918cebb3 1868 write(rodent.mremcfd, &key, 1);
984263bc
MD
1869 }
1870 return 0;
1871 }
1872
918cebb3
MS
1873 act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1874 act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
984263bc
MD
1875 break;
1876
1877 case MOUSE_PROTO_GLIDEPOINT: /* GlidePoint */
1878 case MOUSE_PROTO_THINK: /* ThinkingMouse */
1879 case MOUSE_PROTO_INTELLI: /* IntelliMouse, NetMouse, Mie Mouse,
1880 MouseMan+ */
1881 act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
918cebb3
MS
1882 | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
1883 act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
1884 act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
984263bc 1885 break;
918cebb3 1886
984263bc 1887 case MOUSE_PROTO_MSC: /* MouseSystems Corp */
2cc577c1 1888#if 0 /* not yet */
984263bc
MD
1889 case MOUSE_PROTO_MARIQUA: /* Mariqua */
1890#endif
1891 act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
918cebb3
MS
1892 act->dx = (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
1893 act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
984263bc
MD
1894 break;
1895
1896 case MOUSE_PROTO_JOGDIAL: /* JogDial */
1897 if (rBuf == 0x6c)
918cebb3 1898 act->dz = -1;
984263bc 1899 if (rBuf == 0x72)
918cebb3 1900 act->dz = 1;
984263bc
MD
1901 if (rBuf == 0x64)
1902 act->button = MOUSE_BUTTON1DOWN;
1903 if (rBuf == 0x75)
1904 act->button = 0;
1905 break;
1906
1907 case MOUSE_PROTO_HITTAB: /* MM HitTablet */
1908 act->button = butmaphit[pBuf[0] & 0x07];
1909 act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1];
1910 act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2];
1911 break;
1912
1913 case MOUSE_PROTO_MM: /* MM Series */
1914 case MOUSE_PROTO_LOGI: /* Logitech Mice */
1915 act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
1916 act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1];
1917 act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2];
1918 break;
918cebb3 1919
984263bc
MD
1920 case MOUSE_PROTO_VERSAPAD: /* VersaPad */
1921 act->button = butmapversa[(pBuf[0] & MOUSE_VERSA_BUTTONS) >> 3];
1922 act->button |= (pBuf[0] & MOUSE_VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
1923 act->dx = act->dy = 0;
1924 if (!(pBuf[0] & MOUSE_VERSA_IN_USE)) {
1925 on = FALSE;
1926 break;
1927 }
1928 x = (pBuf[2] << 6) | pBuf[1];
1929 if (x & 0x800)
1930 x -= 0x1000;
1931 y = (pBuf[4] << 6) | pBuf[3];
1932 if (y & 0x800)
1933 y -= 0x1000;
1934 if (on) {
1935 act->dx = prev_x - x;
1936 act->dy = prev_y - y;
1937 } else {
1938 on = TRUE;
1939 }
1940 prev_x = x;
1941 prev_y = y;
1942 break;
1943
1944 case MOUSE_PROTO_BUS: /* Bus */
1945 case MOUSE_PROTO_INPORT: /* InPort */
1946 act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
918cebb3
MS
1947 act->dx = (signed char)pBuf[1];
1948 act->dy = - (signed char)pBuf[2];
984263bc
MD
1949 break;
1950
1951 case MOUSE_PROTO_PS2: /* PS/2 */
1952 act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
1953 act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ? pBuf[1] - 256 : pBuf[1];
1954 act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ? -(pBuf[2] - 256) : -pBuf[2];
1955 /*
1956 * Moused usually operates the psm driver at the operation level 1
1957 * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
918cebb3
MS
1958 * The following code takes effect only when the user explicitly
1959 * requets the level 2 at which wheel movement and additional button
984263bc
MD
1960 * actions are encoded in model-dependent formats. At the level 0
1961 * the following code is no-op because the psm driver says the model
1962 * is MOUSE_MODEL_GENERIC.
1963 */
1964 switch (rodent.hw.model) {
1965 case MOUSE_MODEL_EXPLORER:
1966 /* wheel and additional button data is in the fourth byte */
1967 act->dz = (pBuf[3] & MOUSE_EXPLORER_ZNEG)
1968 ? (pBuf[3] & 0x0f) - 16 : (pBuf[3] & 0x0f);
1969 act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON4DOWN)
1970 ? MOUSE_BUTTON4DOWN : 0;
1971 act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON5DOWN)
1972 ? MOUSE_BUTTON5DOWN : 0;
1973 break;
1974 case MOUSE_MODEL_INTELLI:
1975 case MOUSE_MODEL_NET:
1976 /* wheel data is in the fourth byte */
918cebb3 1977 act->dz = (signed char)pBuf[3];
984263bc
MD
1978 if ((act->dz >= 7) || (act->dz <= -7))
1979 act->dz = 0;
1980 /* some compatible mice may have additional buttons */
1981 act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON4DOWN)
1982 ? MOUSE_BUTTON4DOWN : 0;
1983 act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON5DOWN)
1984 ? MOUSE_BUTTON5DOWN : 0;
1985 break;
1986 case MOUSE_MODEL_MOUSEMANPLUS:
1987 if (((pBuf[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
1988 && (abs(act->dx) > 191)
1989 && MOUSE_PS2PLUS_CHECKBITS(pBuf)) {
1990 /* the extended data packet encodes button and wheel events */
1991 switch (MOUSE_PS2PLUS_PACKET_TYPE(pBuf)) {
1992 case 1:
1993 /* wheel data packet */
1994 act->dx = act->dy = 0;
1995 if (pBuf[2] & 0x80) {
1996 /* horizontal roller count - ignore it XXX*/
1997 } else {
1998 /* vertical roller count */
1999 act->dz = (pBuf[2] & MOUSE_PS2PLUS_ZNEG)
2000 ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
2001 }
2002 act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
2003 ? MOUSE_BUTTON4DOWN : 0;
2004 act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
2005 ? MOUSE_BUTTON5DOWN : 0;
2006 break;
2007 case 2:
2008 /* this packet type is reserved by Logitech */
2009 /*
2010 * IBM ScrollPoint Mouse uses this packet type to
2011 * encode both vertical and horizontal scroll movement.
2012 */
2013 act->dx = act->dy = 0;
2014 /* horizontal roller count */
2015 if (pBuf[2] & 0x0f)
2016 act->dz = (pBuf[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
2017 /* vertical roller count */
2018 if (pBuf[2] & 0xf0)
2019 act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
2020#if 0
2021 /* vertical roller count */
2022 act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG)
2023 ? ((pBuf[2] >> 4) & 0x0f) - 16
2024 : ((pBuf[2] >> 4) & 0x0f);
2025 /* horizontal roller count */
2026 act->dw = (pBuf[2] & MOUSE_SPOINT_WNEG)
2027 ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
2028#endif
2029 break;
2030 case 0:
2031 /* device type packet - shouldn't happen */
918cebb3 2032 /* FALLTHROUGH */
984263bc
MD
2033 default:
2034 act->dx = act->dy = 0;
2035 act->button = act->obutton;
918cebb3 2036 debug("unknown PS2++ packet type %d: 0x%02x 0x%02x 0x%02x\n",
984263bc
MD
2037 MOUSE_PS2PLUS_PACKET_TYPE(pBuf),
2038 pBuf[0], pBuf[1], pBuf[2]);
2039 break;
2040 }
2041 } else {
2042 /* preserve button states */
2043 act->button |= act->obutton & MOUSE_EXTBUTTONS;
2044 }
2045 break;
2046 case MOUSE_MODEL_GLIDEPOINT:
2047 /* `tapping' action */
2048 act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
2049 break;
2050 case MOUSE_MODEL_NETSCROLL:
2051 /* three addtional bytes encode buttons and wheel events */
2052 act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
2053 ? MOUSE_BUTTON4DOWN : 0;
2054 act->button |= (pBuf[3] & MOUSE_PS2_BUTTON1DOWN)
2055 ? MOUSE_BUTTON5DOWN : 0;
2056 act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
2057 break;
2058 case MOUSE_MODEL_THINK:
2059 /* the fourth button state in the first byte */
2060 act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
2061 break;
2062 case MOUSE_MODEL_VERSAPAD:
2063 act->button = butmapversaps2[pBuf[0] & MOUSE_PS2VERSA_BUTTONS];
2064 act->button |=
2065 (pBuf[0] & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
2066 act->dx = act->dy = 0;
2067 if (!(pBuf[0] & MOUSE_PS2VERSA_IN_USE)) {
2068 on = FALSE;
2069 break;
2070 }
2071 x = ((pBuf[4] << 8) & 0xf00) | pBuf[1];
2072 if (x & 0x800)
2073 x -= 0x1000;
2074 y = ((pBuf[4] << 4) & 0xf00) | pBuf[2];
2075 if (y & 0x800)
2076 y -= 0x1000;
2077 if (on) {
2078 act->dx = prev_x - x;
2079 act->dy = prev_y - y;
2080 } else {
2081 on = TRUE;
2082 }
2083 prev_x = x;
2084 prev_y = y;
2085 break;
2086 case MOUSE_MODEL_4D:
2087 act->dx = (pBuf[1] & 0x80) ? pBuf[1] - 256 : pBuf[1];
2088 act->dy = (pBuf[2] & 0x80) ? -(pBuf[2] - 256) : -pBuf[2];
2089 switch (pBuf[0] & MOUSE_4D_WHEELBITS) {
2090 case 0x10:
2091 act->dz = 1;
2092 break;
2093 case 0x30:
2094 act->dz = -1;
2095 break;
2096 case 0x40: /* 2nd wheel rolling right XXX */
2097 act->dz = 2;
2098 break;
2099 case 0xc0: /* 2nd wheel rolling left XXX */
2100 act->dz = -2;
2101 break;
2102 }
2103 break;
2104 case MOUSE_MODEL_4DPLUS:
2105 if ((act->dx < 16 - 256) && (act->dy > 256 - 16)) {
2106 act->dx = act->dy = 0;
2107 if (pBuf[2] & MOUSE_4DPLUS_BUTTON4DOWN)
2108 act->button |= MOUSE_BUTTON4DOWN;
2109 act->dz = (pBuf[2] & MOUSE_4DPLUS_ZNEG)
2110 ? ((pBuf[2] & 0x07) - 8) : (pBuf[2] & 0x07);
2111 } else {
2112 /* preserve previous button states */
2113 act->button |= act->obutton & MOUSE_EXTBUTTONS;
2114 }
2115 break;
2116 case MOUSE_MODEL_GENERIC:
2117 default:
2118 break;
2119 }
2120 break;
2121
2122 case MOUSE_PROTO_SYSMOUSE: /* sysmouse */
2123 act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
918cebb3
MS
2124 act->dx = (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
2125 act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
984263bc 2126 if (rodent.level == 1) {
918cebb3 2127 act->dz = ((signed char)(pBuf[5] << 1) + (signed char)(pBuf[6] << 1)) >> 1;
984263bc
MD
2128 act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
2129 }
2130 break;
2131
2132 default:
2133 return 0;
2134 }
918cebb3 2135 /*
984263bc
MD
2136 * We don't reset pBufP here yet, as there may be an additional data
2137 * byte in some protocols. See above.
2138 */
2139
2140 /* has something changed? */
2141 act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
2142 | (act->obutton ^ act->button);
2143
2144 return act->flags;
2145}
2146
2147static int
2148r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans)
2149{
2150 int changed;
2151 int flags;
2152
2153 a2->dx = a1->dx;
2154 a2->dy = a1->dy;
2155 a2->dz = a1->dz;
2156 a2->obutton = a2->button;
2157 a2->button = a1->button;
2158 a2->flags = a1->flags;
2159 changed = FALSE;
2160
2161 if (rodent.flags & Emulate3Button) {
2162 if (debug > 2)
918cebb3 2163 debug("state:%d, trans:%d -> state:%d",
984263bc
MD
2164 mouse_button_state, trans,
2165 states[mouse_button_state].s[trans]);
2166 /*
2167 * Avoid re-ordering button and movement events. While a button
2168 * event is deferred, throw away up to BUTTON2_MAXMOVE movement
2169 * events to allow for mouse jitter. If more movement events
2170 * occur, then complete the deferred button events immediately.
2171 */
2172 if ((a2->dx != 0 || a2->dy != 0) &&
2173 S_DELAYED(states[mouse_button_state].s[trans])) {
2174 if (++mouse_move_delayed > BUTTON2_MAXMOVE) {
2175 mouse_move_delayed = 0;
2176 mouse_button_state =
2177 states[mouse_button_state].s[A_TIMEOUT];
2178 changed = TRUE;
2179 } else
2180 a2->dx = a2->dy = 0;
2181 } else
2182 mouse_move_delayed = 0;
2183 if (mouse_button_state != states[mouse_button_state].s[trans])
2184 changed = TRUE;
2185 if (changed)
2186 gettimeofday(&mouse_button_state_tv, NULL);
2187 mouse_button_state = states[mouse_button_state].s[trans];
2188 a2->button &=
2189 ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN);
2190 a2->button &= states[mouse_button_state].mask;
2191 a2->button |= states[mouse_button_state].buttons;
2192 flags = a2->flags & MOUSE_POSCHANGED;
2193 flags |= a2->obutton ^ a2->button;
2194 if (flags & MOUSE_BUTTON2DOWN) {
2195 a2->flags = flags & MOUSE_BUTTON2DOWN;
2196 r_timestamp(a2);
2197 }
2198 a2->flags = flags;
2199 }
2200 return changed;
2201}
2202
2203/* phisical to logical button mapping */
2204static int p2l[MOUSE_MAXBUTTON] = {
918cebb3
MS
2205 MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
2206 MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
984263bc
MD
2207 0x00000100, 0x00000200, 0x00000400, 0x00000800,
2208 0x00001000, 0x00002000, 0x00004000, 0x00008000,
2209 0x00010000, 0x00020000, 0x00040000, 0x00080000,
2210 0x00100000, 0x00200000, 0x00400000, 0x00800000,
2211 0x01000000, 0x02000000, 0x04000000, 0x08000000,
2212 0x10000000, 0x20000000, 0x40000000,
2213};
2214
2215static char *
2216skipspace(char *s)
2217{
2218 while(isspace(*s))
2219 ++s;
2220 return s;
2221}
2222
2223static int
2224r_installmap(char *arg)
2225{
2226 int pbutton;
2227 int lbutton;
2228 char *s;
2229
2230 while (*arg) {
2231 arg = skipspace(arg);
2232 s = arg;
2233 while (isdigit(*arg))
2234 ++arg;
2235 arg = skipspace(arg);
2236 if ((arg <= s) || (*arg != '='))
2237 return FALSE;
2238 lbutton = atoi(s);
2239
2240 arg = skipspace(++arg);
2241 s = arg;
2242 while (isdigit(*arg))
2243 ++arg;
2244 if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
2245 return FALSE;
2246 pbutton = atoi(s);
2247
2248 if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
2249 return FALSE;
2250 if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
2251 return FALSE;
2252 p2l[pbutton - 1] = 1 << (lbutton - 1);
2253 mstate[lbutton - 1] = &bstate[pbutton - 1];
2254 }
2255
2256 return TRUE;
2257}
2258
2259static void
2260r_map(mousestatus_t *act1, mousestatus_t *act2)
2261{
918cebb3
MS
2262 register int pb;
2263 register int pbuttons;
984263bc
MD
2264 int lbuttons;
2265
2266 pbuttons = act1->button;
2267 lbuttons = 0;
2268
2269 act2->obutton = act2->button;
2270 if (pbuttons & rodent.wmode) {
2271 pbuttons &= ~rodent.wmode;
2272 act1->dz = act1->dy;
2273 act1->dx = 0;
2274 act1->dy = 0;
2275 }
2276 act2->dx = act1->dx;
2277 act2->dy = act1->dy;
2278 act2->dz = act1->dz;
2279
2280 switch (rodent.zmap[0]) {
2281 case 0: /* do nothing */
2282 break;
2283 case MOUSE_XAXIS:
2284 if (act1->dz != 0) {
2285 act2->dx = act1->dz;
2286 act2->dz = 0;
2287 }
2288 break;
2289 case MOUSE_YAXIS:
2290 if (act1->dz != 0) {
2291 act2->dy = act1->dz;
2292 act2->dz = 0;
2293 }
2294 break;
2295 default: /* buttons */
2296 pbuttons &= ~(rodent.zmap[0] | rodent.zmap[1]
2297 | rodent.zmap[2] | rodent.zmap[3]);
2298 if ((act1->dz < -1) && rodent.zmap[2]) {
2299 pbuttons |= rodent.zmap[2];
2300 zstate[2].count = 1;
2301 } else if (act1->dz < 0) {
2302 pbuttons |= rodent.zmap[0];
2303 zstate[0].count = 1;
2304 } else if ((act1->dz > 1) && rodent.zmap[3]) {
2305 pbuttons |= rodent.zmap[3];
2306 zstate[3].count = 1;
2307 } else if (act1->dz > 0) {
2308 pbuttons |= rodent.zmap[1];
2309 zstate[1].count = 1;
2310 }
2311 act2->dz = 0;
2312 break;
2313 }
2314
2315 for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
2316 lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
2317 pbuttons >>= 1;
2318 }
2319 act2->button = lbuttons;
2320
2321 act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
2322 | (act2->obutton ^ act2->button);
2323}
2324
2325static void
2326r_timestamp(mousestatus_t *act)
2327{
2328 struct timeval tv;
2329 struct timeval tv1;
2330 struct timeval tv2;
2331 struct timeval tv3;
2332 int button;
2333 int mask;
2334 int i;
2335
2336 mask = act->flags & MOUSE_BUTTONS;
984263bc
MD
2337 if (mask == 0)
2338 return;
984263bc
MD
2339
2340 gettimeofday(&tv1, NULL);
2341
2342 /* double click threshold */
2343 tv2.tv_sec = rodent.clickthreshold/1000;
2344 tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
918cebb3 2345 timersub(&tv1, &tv2, &tv);
984263bc
MD
2346 debug("tv: %ld %ld", tv.tv_sec, tv.tv_usec);
2347
2348 /* 3 button emulation timeout */
2349 tv2.tv_sec = rodent.button2timeout/1000;
2350 tv2.tv_usec = (rodent.button2timeout%1000)*1000;
918cebb3 2351 timersub(&tv1, &tv2, &tv3);
984263bc
MD
2352
2353 button = MOUSE_BUTTON1DOWN;
2354 for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
918cebb3
MS
2355 if (mask & 1) {
2356 if (act->button & button) {
2357 /* the button is down */
2358 debug(" : %ld %ld",
984263bc
MD
2359 bstate[i].tv.tv_sec, bstate[i].tv.tv_usec);
2360 if (timercmp(&tv, &bstate[i].tv, >)) {
918cebb3
MS
2361 bstate[i].count = 1;
2362 } else {
2363 ++bstate[i].count;
2364 }
984263bc 2365 bstate[i].tv = tv1;
918cebb3
MS
2366 } else {
2367 /* the button is up */
2368 bstate[i].tv = tv1;
2369 }
2370 } else {
984263bc
MD
2371 if (act->button & button) {
2372 /* the button has been down */
2373 if (timercmp(&tv3, &bstate[i].tv, >)) {
2374 bstate[i].count = 1;
2375 bstate[i].tv = tv1;
2376 act->flags |= button;
2377 debug("button %d timeout", i + 1);
2378 }
2379 } else {
2380 /* the button has been up */
2381 }
2382 }
2383 button <<= 1;
2384 mask >>= 1;
2385 }
2386}
2387
2388static int
2389r_timeout(void)
2390{
2391 struct timeval tv;
2392 struct timeval tv1;
2393 struct timeval tv2;
2394
2395 if (states[mouse_button_state].timeout)
2396 return TRUE;
2397 gettimeofday(&tv1, NULL);
2398 tv2.tv_sec = rodent.button2timeout/1000;
2399 tv2.tv_usec = (rodent.button2timeout%1000)*1000;
918cebb3 2400 timersub(&tv1, &tv2, &tv);
984263bc
MD
2401 return timercmp(&tv, &mouse_button_state_tv, >);
2402}
2403
2404static void
2405r_click(mousestatus_t *act)
2406{
2407 struct mouse_info mouse;
2408 int button;
2409 int mask;
2410 int i;
2411
2412 mask = act->flags & MOUSE_BUTTONS;
2413 if (mask == 0)
2414 return;
2415
2416 button = MOUSE_BUTTON1DOWN;
2417 for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
918cebb3 2418 if (mask & 1) {
984263bc 2419 debug("mstate[%d]->count:%d", i, mstate[i]->count);
918cebb3
MS
2420 if (act->button & button) {
2421 /* the button is down */
2422 mouse.u.event.value = mstate[i]->count;
2423 } else {
2424 /* the button is up */
2425 mouse.u.event.value = 0;
2426 }
984263bc
MD
2427 mouse.operation = MOUSE_BUTTON_EVENT;
2428 mouse.u.event.id = button;
2429 if (debug < 2)
918cebb3 2430 ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
984263bc 2431 debug("button %d count %d", i + 1, mouse.u.event.value);
918cebb3 2432 }
984263bc
MD
2433 button <<= 1;
2434 mask >>= 1;
2435 }
2436}
2437
2438/* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
2439/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
2440/*
2441 * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
2442 *
2443 * Permission to use, copy, modify, distribute, and sell this software and its
2444 * documentation for any purpose is hereby granted without fee, provided that
2445 * the above copyright notice appear in all copies and that both that
2446 * copyright notice and this permission notice appear in supporting
918cebb3
MS
2447 * documentation, and that the name of David Dawes
2448 * not be used in advertising or publicity pertaining to distribution of
984263bc 2449 * the software without specific, written prior permission.
918cebb3
MS
2450 * David Dawes makes no representations about the suitability of this
2451 * software for any purpose. It is provided "as is" without express or
984263bc
MD
2452 * implied warranty.
2453 *
918cebb3
MS
2454 * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
2455 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2456 * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
2457 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
2458 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
2459 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
984263bc
MD
2460 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2461 *
2462 */
2463
2464
2465static void
2466setmousespeed(int old, int new, unsigned cflag)
2467{
2468 struct termios tty;
2469 char *c;
2470
2471 if (tcgetattr(rodent.mfd, &tty) < 0)
2472 {
918cebb3 2473 logwarn("unable to get status of mouse fd");
984263bc
MD
2474 return;
2475 }
2476
2477 tty.c_iflag = IGNBRK | IGNPAR;
2478 tty.c_oflag = 0;
2479 tty.c_lflag = 0;
2480 tty.c_cflag = (tcflag_t)cflag;
2481 tty.c_cc[VTIME] = 0;
2482 tty.c_cc[VMIN] = 1;
2483
2484 switch (old)
2485 {
2486 case 9600:
2487 cfsetispeed(&tty, B9600);
2488 cfsetospeed(&tty, B9600);
2489 break;
2490 case 4800:
2491 cfsetispeed(&tty, B4800);
2492 cfsetospeed(&tty, B4800);
2493 break;
2494 case 2400:
2495 cfsetispeed(&tty, B2400);
2496 cfsetospeed(&tty, B2400);
2497 break;
2498 case 1200:
2499 default:
2500 cfsetispeed(&tty, B1200);
2501 cfsetospeed(&tty, B1200);
2502 }
2503
2504 if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
2505 {
918cebb3 2506 logwarn("unable to set status of mouse fd");
984263bc
MD
2507 return;
2508 }
2509
2510 switch (new)
2511 {
2512 case 9600:
2513 c = "*q";
2514 cfsetispeed(&tty, B9600);
2515 cfsetospeed(&tty, B9600);
2516 break;
2517 case 4800:
2518 c = "*p";
2519 cfsetispeed(&tty, B4800);
2520 cfsetospeed(&tty, B4800);
2521 break;
2522 case 2400:
2523 c = "*o";
2524 cfsetispeed(&tty, B2400);
2525 cfsetospeed(&tty, B2400);
2526 break;
2527 case 1200:
2528 default:
2529 c = "*n";
2530 cfsetispeed(&tty, B1200);
2531 cfsetospeed(&tty, B1200);
2532 }
2533
918cebb3 2534 if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
984263bc
MD
2535 || rodent.rtype == MOUSE_PROTO_LOGI)
2536 {
2537 if (write(rodent.mfd, c, 2) != 2)
2538 {
918cebb3 2539 logwarn("unable to write to mouse fd");
984263bc
MD
2540 return;
2541 }
2542 }
2543 usleep(100000);
2544
2545 if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
918cebb3 2546 logwarn("unable to set status of mouse fd");
984263bc
MD
2547}
2548
918cebb3
MS
2549/*
2550 * PnP COM device support
2551 *
984263bc
MD
2552 * It's a simplistic implementation, but it works :-)
2553 * KY, 31/7/97.
2554 */
2555
2556/*
918cebb3
MS
2557 * Try to elicit a PnP ID as described in
2558 * Microsoft, Hayes: "Plug and Play External COM Device Specification,
984263bc
MD
2559 * rev 1.00", 1995.
2560 *
2561 * The routine does not fully implement the COM Enumerator as par Section
2562 * 2.1 of the document. In particular, we don't have idle state in which
918cebb3
MS
2563 * the driver software monitors the com port for dynamic connection or
2564 * removal of a device at the port, because `moused' simply quits if no
984263bc
MD
2565 * device is found.
2566 *
918cebb3 2567 * In addition, as PnP COM device enumeration procedure slightly has
984263bc 2568 * changed since its first publication, devices which follow earlier
918cebb3 2569 * revisions of the above spec. may fail to respond if the rev 1.0
984263bc
MD
2570 * procedure is used. XXX
2571 */
2572static int
2573pnpwakeup1(void)
2574{
2575 struct timeval timeout;
2576 fd_set fds;
2577 int i;
2578
918cebb3 2579 /*
984263bc
MD
2580 * This is the procedure described in rev 1.0 of PnP COM device spec.
2581 * Unfortunately, some devices which comform to earlier revisions of
2582 * the spec gets confused and do not return the ID string...
2583 */
2584 debug("PnP COM device rev 1.0 probe...");
2585
2586 /* port initialization (2.1.2) */
2587 ioctl(rodent.mfd, TIOCMGET, &i);
2588 i |= TIOCM_DTR; /* DTR = 1 */
2589 i &= ~TIOCM_RTS; /* RTS = 0 */
2590 ioctl(rodent.mfd, TIOCMSET, &i);
2591 usleep(240000);
2592
2593 /*
918cebb3
MS
2594 * The PnP COM device spec. dictates that the mouse must set DSR
2595 * in response to DTR (by hardware or by software) and that if DSR is
984263bc
MD
2596 * not asserted, the host computer should think that there is no device
2597 * at this serial port. But some mice just don't do that...
2598 */
2599 ioctl(rodent.mfd, TIOCMGET, &i);
2600 debug("modem status 0%o", i);
2601 if ((i & TIOCM_DSR) == 0)
2602 return FALSE;
2603
2604 /* port setup, 1st phase (2.1.3) */
2605 setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
2606 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
2607 ioctl(rodent.mfd, TIOCMBIC, &i);
2608 usleep(240000);
2609 i = TIOCM_DTR; /* DTR = 1, RTS = 0 */
2610 ioctl(rodent.mfd, TIOCMBIS, &i);
2611 usleep(240000);
2612
2613 /* wait for response, 1st phase (2.1.4) */
2614 i = FREAD;
2615 ioctl(rodent.mfd, TIOCFLUSH, &i);
2616 i = TIOCM_RTS; /* DTR = 1, RTS = 1 */
2617 ioctl(rodent.mfd, TIOCMBIS, &i);
2618
2619 /* try to read something */
2620 FD_ZERO(&fds);
2621 FD_SET(rodent.mfd, &fds);
2622 timeout.tv_sec = 0;
2623 timeout.tv_usec = 240000;
2624 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2625 debug("pnpwakeup1(): valid response in first phase.");
2626 return TRUE;
2627 }
2628
2629 /* port setup, 2nd phase (2.1.5) */
2630 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
2631 ioctl(rodent.mfd, TIOCMBIC, &i);
2632 usleep(240000);
2633
2634 /* wait for respose, 2nd phase (2.1.6) */
2635 i = FREAD;
2636 ioctl(rodent.mfd, TIOCFLUSH, &i);
2637 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
2638 ioctl(rodent.mfd, TIOCMBIS, &i);
2639
2640 /* try to read something */
2641 FD_ZERO(&fds);
2642 FD_SET(rodent.mfd, &fds);
2643 timeout.tv_sec = 0;
2644 timeout.tv_usec = 240000;
2645 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2646 debug("pnpwakeup1(): valid response in second phase.");
2647 return TRUE;
2648 }
2649
2650 return FALSE;
2651}
2652
2653static int
2654pnpwakeup2(void)
2655{
2656 struct timeval timeout;
2657 fd_set fds;
2658 int i;
2659
2660 /*
2661 * This is a simplified procedure; it simply toggles RTS.
2662 */
2663 debug("alternate probe...");
2664
2665 ioctl(rodent.mfd, TIOCMGET, &i);
2666 i |= TIOCM_DTR; /* DTR = 1 */
2667 i &= ~TIOCM_RTS; /* RTS = 0 */
2668 ioctl(rodent.mfd, TIOCMSET, &i);
2669 usleep(240000);
2670
2671 setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
2672
2673 /* wait for respose */
2674 i = FREAD;
2675 ioctl(rodent.mfd, TIOCFLUSH, &i);
2676 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
2677 ioctl(rodent.mfd, TIOCMBIS, &i);
2678
2679 /* try to read something */
2680 FD_ZERO(&fds);
2681 FD_SET(rodent.mfd, &fds);
2682 timeout.tv_sec = 0;
2683 timeout.tv_usec = 240000;
2684 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
2685 debug("pnpwakeup2(): valid response.");
2686 return TRUE;
2687 }
2688
2689 return FALSE;
2690}
2691
2692static int
2693pnpgets(char *buf)
2694{
2695 struct timeval timeout;
2696 fd_set fds;
2697 int begin;
2698 int i;
2699 char c;
2700
2701 if (!pnpwakeup1() && !pnpwakeup2()) {
2702 /*
918cebb3
MS
2703 * According to PnP spec, we should set DTR = 1 and RTS = 0 while
2704 * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed,
2705 * assuming there is something at the port even if it didn't
984263bc
MD
2706 * respond to the PnP enumeration procedure.
2707 */
984263bc
MD
2708 i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
2709 ioctl(rodent.mfd, TIOCMBIS, &i);
2710 return 0;
2711 }
2712
2713 /* collect PnP COM device ID (2.1.7) */
2714 begin = -1;
2715 i = 0;
2716 usleep(240000); /* the mouse must send `Begin ID' within 200msec */
2717 while (read(rodent.mfd, &c, 1) == 1) {
2718 /* we may see "M", or "M3..." before `Begin ID' */
2719 buf[i++] = c;
918cebb3 2720 if ((c == 0x08) || (c == 0x28)) { /* Begin ID */
984263bc
MD
2721 debug("begin-id %02x", c);
2722 begin = i - 1;
2723 break;
918cebb3
MS
2724 }
2725 debug("%c %02x", c, c);
984263bc
MD
2726 if (i >= 256)
2727 break;
2728 }
2729 if (begin < 0) {
2730 /* we haven't seen `Begin ID' in time... */
2731 goto connect_idle;
2732 }
2733
2734 ++c; /* make it `End ID' */
2735 for (;;) {
918cebb3
MS
2736 FD_ZERO(&fds);
2737 FD_SET(rodent.mfd, &fds);
2738 timeout.tv_sec = 0;
2739 timeout.tv_usec = 240000;
2740 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
984263bc
MD
2741 break;
2742
2743 read(rodent.mfd, &buf[i], 1);
918cebb3 2744 if (buf[i++] == c) /* End ID */
984263bc
MD
2745 break;
2746 if (i >= 256)
2747 break;
2748 }
2749 if (begin > 0) {
2750 i -= begin;
2751 bcopy(&buf[begin], &buf[0], i);
2752 }
2753 /* string may not be human readable... */
2754 debug("len:%d, '%-*.*s'", i, i, i, buf);
2755
2756 if (buf[i - 1] == c)
2757 return i; /* a valid PnP string */
2758
2759 /*
918cebb3 2760 * According to PnP spec, we should set DTR = 1 and RTS = 0 while
984263bc
MD
2761 * in idle state. But, `moused' shall leave the modem control lines
2762 * as they are. See above.
2763 */
2764connect_idle:
2765
2766 /* we may still have something in the buffer */
2767 return ((i > 0) ? i : 0);
2768}
2769
2770static int
2771pnpparse(pnpid_t *id, char *buf, int len)
2772{
2773 char s[3];
2774 int offset;
2775 int sum = 0;
2776 int i, j;
2777
2778 id->revision = 0;
2779 id->eisaid = NULL;
2780 id->serial = NULL;
2781 id->class = NULL;
2782 id->compat = NULL;
2783 id->description = NULL;
2784 id->neisaid = 0;
2785 id->nserial = 0;
2786 id->nclass = 0;
2787 id->ncompat = 0;
2788 id->ndescription = 0;
2789
2790 if ((buf[0] != 0x28) && (buf[0] != 0x08)) {
2791 /* non-PnP mice */
2792 switch(buf[0]) {
2793 default:
2794 return FALSE;
2795 case 'M': /* Microsoft */
2796 id->eisaid = "PNP0F01";
2797 break;
2798 case 'H': /* MouseSystems */
2799 id->eisaid = "PNP0F04";
2800 break;
2801 }
2802 id->neisaid = strlen(id->eisaid);
2803 id->class = "MOUSE";
2804 id->nclass = strlen(id->class);
2805 debug("non-PnP mouse '%c'", buf[0]);
2806 return TRUE;
2807 }
2808
2809 /* PnP mice */
2810 offset = 0x28 - buf[0];
2811
2812 /* calculate checksum */
2813 for (i = 0; i < len - 3; ++i) {
2814 sum += buf[i];
2815 buf[i] += offset;
2816 }
2817 sum += buf[len - 1];
2818 for (; i < len; ++i)
2819 buf[i] += offset;
2820 debug("PnP ID string: '%*.*s'", len, len, buf);
2821
2822 /* revision */
2823 buf[1] -= offset;
2824 buf[2] -= offset;
2825 id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
2826 debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
2827
918cebb3 2828 /* EISA vender and product ID */
984263bc
MD
2829 id->eisaid = &buf[3];
2830 id->neisaid = 7;
2831
2832 /* option strings */
2833 i = 10;
2834 if (buf[i] == '\\') {
918cebb3
MS
2835 /* device serial # */
2836 for (j = ++i; i < len; ++i) {
2837 if (buf[i] == '\\')
984263bc 2838 break;
918cebb3 2839 }
984263bc
MD
2840 if (i >= len)
2841 i -= 3;
2842 if (i - j == 8) {
918cebb3
MS
2843 id->serial = &buf[j];
2844 id->nserial = 8;
984263bc
MD
2845 }
2846 }
2847 if (buf[i] == '\\') {
918cebb3
MS
2848 /* PnP class */
2849 for (j = ++i; i < len; ++i) {
2850 if (buf[i] == '\\')
984263bc 2851 break;
918cebb3 2852 }
984263bc
MD
2853 if (i >= len)
2854 i -= 3;
2855 if (i > j + 1) {
918cebb3
MS
2856 id->class = &buf[j];
2857 id->nclass = i - j;
2858 }
984263bc
MD
2859 }
2860 if (buf[i] == '\\') {
2861 /* compatible driver */
918cebb3
MS
2862 for (j = ++i; i < len; ++i) {
2863 if (buf[i] == '\\')
984263bc 2864 break;
918cebb3 2865 }
984263bc 2866 /*
918cebb3 2867 * PnP COM spec prior to v0.96 allowed '*' in this field,
984263bc
MD
2868 * it's not allowed now; just igore it.
2869 */
2870 if (buf[j] == '*')
2871 ++j;
2872 if (i >= len)
2873 i -= 3;
2874 if (i > j + 1) {
918cebb3
MS
2875 id->compat = &buf[j];
2876 id->ncompat = i - j;
2877 }
984263bc
MD
2878 }
2879 if (buf[i] == '\\') {
2880 /* product description */
918cebb3
MS
2881 for (j = ++i; i < len; ++i) {
2882 if (buf[i] == ';')
984263bc 2883 break;
918cebb3 2884 }
984263bc
MD
2885 if (i >= len)
2886 i -= 3;
2887 if (i > j + 1) {
918cebb3
MS
2888 id->description = &buf[j];
2889 id->ndescription = i - j;
2890 }
984263bc
MD
2891 }
2892
2893 /* checksum exists if there are any optional fields */
2894 if ((id->nserial > 0) || (id->nclass > 0)
2895 || (id->ncompat > 0) || (id->ndescription > 0)) {
918cebb3
MS
2896 debug("PnP checksum: 0x%X", sum);
2897 sprintf(s, "%02X", sum & 0x0ff);
2898 if (strncmp(s, &buf[len - 3], 2) != 0) {
984263bc 2899#if 0
918cebb3
MS
2900 /*
2901 * I found some mice do not comply with the PnP COM device
984263bc
MD
2902 * spec regarding checksum... XXX
2903 */
918cebb3 2904 logwarnx("PnP checksum error", 0);
984263bc
MD
2905 return FALSE;
2906#endif
918cebb3 2907 }
984263bc
MD
2908 }
2909
2910 return TRUE;
2911}
2912
2913static symtab_t *
2914pnpproto(pnpid_t *id)
2915{
2916 symtab_t *t;
2917 int i, j;
2918
2919 if (id->nclass > 0)
918cebb3
MS
2920 if (strncmp(id->class, "MOUSE", id->nclass) != 0 &&
2921 strncmp(id->class, "TABLET", id->nclass) != 0)
984263bc
MD
2922 /* this is not a mouse! */
2923 return NULL;
2924
2925 if (id->neisaid > 0) {
918cebb3 2926 t = gettoken(pnpprod, id->eisaid, id->neisaid);
984263bc 2927 if (t->val != MOUSE_PROTO_UNKNOWN)
918cebb3 2928 return t;
984263bc
MD
2929 }
2930
2931 /*
2932 * The 'Compatible drivers' field may contain more than one
2933 * ID separated by ','.
2934 */
2935 if (id->ncompat <= 0)
2936 return NULL;
2937 for (i = 0; i < id->ncompat; ++i) {
918cebb3
MS
2938 for (j = i; id->compat[i] != ','; ++i)
2939 if (i >= id->ncompat)
984263bc 2940 break;
918cebb3
MS
2941 if (i > j) {
2942 t = gettoken(pnpprod, id->compat + j, i - j);
984263bc 2943 if (t->val != MOUSE_PROTO_UNKNOWN)
918cebb3 2944 return t;
984263bc
MD
2945 }
2946 }
2947
2948 return NULL;
2949}
2950
2951/* name/val mapping */
2952
2953static symtab_t *
2954gettoken(symtab_t *tab, char *s, int len)
2955{
2956 int i;
2957
2958 for (i = 0; tab[i].name != NULL; ++i) {
2959 if (strncmp(tab[i].name, s, len) == 0)
2960 break;
2961 }
2962 return &tab[i];
2963}
2964
2965static char *
2966gettokenname(symtab_t *tab, int val)
2967{
2968 int i;
2969
2970 for (i = 0; tab[i].name != NULL; ++i) {
2971 if (tab[i].val == val)
2972 return tab[i].name;
2973 }
2974 return NULL;
2975}
2976
2977
2978/*
2979 * code to read from the Genius Kidspad tablet.
2980
2981The tablet responds to the COM PnP protocol 1.0 with EISA-ID KYE0005,
2982and to pre-pnp probes (RTS toggle) with 'T' (tablet ?)
29839600, 8 bit, parity odd.
2984
2985The tablet puts out 5 bytes. b0 (mask 0xb8, value 0xb8) contains
2986the proximity, tip and button info:
2987 (byte0 & 0x1) true = tip pressed
2988 (byte0 & 0x2) true = button pressed
2989 (byte0 & 0x40) false = pen in proximity of tablet.
2990
2991The next 4 bytes are used for coordinates xl, xh, yl, yh (7 bits valid).
2992
2993Only absolute coordinates are returned, so we use the following approach:
2994we store the last coordinates sent when the pen went out of the tablet,
2995
2996
2997 *
2998 */
2999
3000typedef enum {
3001 S_IDLE, S_PROXY, S_FIRST, S_DOWN, S_UP
3002} k_status ;
3003
3004static int
3005kidspad(u_char rxc, mousestatus_t *act)
3006{
918cebb3 3007 static int buf[5];
984263bc
MD
3008 static int buflen = 0, b_prev = 0 , x_prev = -1, y_prev = -1 ;
3009 static k_status status = S_IDLE ;
77b0c609 3010 static struct timeval now ;
984263bc
MD
3011
3012 int x, y ;
3013
918cebb3 3014 if (buflen > 0 && (rxc & 0x80)) {
984263bc
MD
3015 fprintf(stderr, "invalid code %d 0x%x\n", buflen, rxc);
3016 buflen = 0 ;
3017 }
918cebb3 3018 if (buflen == 0 && (rxc & 0xb8) != 0xb8) {
984263bc
MD
3019 fprintf(stderr, "invalid code 0 0x%x\n", rxc);
3020 return 0 ; /* invalid code, no action */
3021 }
3022 buf[buflen++] = rxc ;
3023 if (buflen < 5)
3024 return 0 ;
3025
3026 buflen = 0 ; /* for next time... */
3027
3028 x = buf[1]+128*(buf[2] - 7) ;
3029 if (x < 0) x = 0 ;
3030 y = 28*128 - (buf[3] + 128* (buf[4] - 7)) ;
3031 if (y < 0) y = 0 ;
3032
3033 x /= 8 ;
3034 y /= 8 ;
3035
3036 act->flags = 0 ;
3037 act->obutton = act->button ;
3038 act->dx = act->dy = act->dz = 0 ;
3039 gettimeofday(&now, NULL);
918cebb3 3040 if (buf[0] & 0x40) /* pen went out of reach */
984263bc
MD
3041 status = S_IDLE ;
3042 else if (status == S_IDLE) { /* pen is newly near the tablet */
3043 act->flags |= MOUSE_POSCHANGED ; /* force update */
3044 status = S_PROXY ;
3045 x_prev = x ;
3046 y_prev = y ;
3047 }
984263bc
MD
3048 act->dx = x - x_prev ;
3049 act->dy = y - y_prev ;
3050 if (act->dx || act->dy)
3051 act->flags |= MOUSE_POSCHANGED ;
3052 x_prev = x ;
3053 y_prev = y ;
3054 if (b_prev != 0 && b_prev != buf[0]) { /* possibly record button change */
3055 act->button = 0 ;
918cebb3 3056 if (buf[0] & 0x01) /* tip pressed */
984263bc 3057 act->button |= MOUSE_BUTTON1DOWN ;
918cebb3 3058 if (buf[0] & 0x02) /* button pressed */
984263bc
MD
3059 act->button |= MOUSE_BUTTON2DOWN ;
3060 act->flags |= MOUSE_BUTTONSCHANGED ;
3061 }
3062 b_prev = buf[0] ;
3063 return act->flags ;
3064}
3065
918cebb3 3066static void
9a92bb4c 3067mremote_serversetup(void)
984263bc
MD
3068{
3069 struct sockaddr_un ad;
3070
3071 /* Open a UNIX domain stream socket to listen for mouse remote clients */
918cebb3 3072 unlink(_PATH_MOUSEREMOTE);
984263bc 3073
918cebb3 3074 if ((rodent.mremsfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
984263bc
MD
3075 logerrx(1, "unable to create unix domain socket %s",_PATH_MOUSEREMOTE);
3076
3077 umask(0111);
918cebb3 3078
984263bc
MD
3079 bzero(&ad, sizeof(ad));
3080 ad.sun_family = AF_UNIX;
3081 strcpy(ad.sun_path, _PATH_MOUSEREMOTE);
3082#ifndef SUN_LEN
918cebb3
MS
3083#define SUN_LEN(unp) (((char *)(unp)->sun_path - (char *)(unp)) + \
3084 strlen((unp)->path))
984263bc 3085#endif
918cebb3 3086 if (bind(rodent.mremsfd, (struct sockaddr *) &ad, SUN_LEN(&ad)) < 0)
984263bc
MD
3087 logerrx(1, "unable to bind unix domain socket %s", _PATH_MOUSEREMOTE);
3088
3089 listen(rodent.mremsfd, 1);
3090}
3091
918cebb3 3092static void
984263bc
MD
3093mremote_clientchg(int add)
3094{
3095 struct sockaddr_un ad;
3096 int ad_len, fd;
3097
3098 if (rodent.rtype != MOUSE_PROTO_X10MOUSEREM)
3099 return;
3100
918cebb3 3101 if (add) {
984263bc
MD
3102 /* Accept client connection, if we don't already have one */
3103 ad_len = sizeof(ad);
3104 fd = accept(rodent.mremsfd, (struct sockaddr *) &ad, &ad_len);
3105 if (fd < 0)
3106 logwarnx("failed accept on mouse remote socket");
3107
918cebb3 3108 if (rodent.mremcfd < 0) {
984263bc
MD
3109 rodent.mremcfd = fd;
3110 debug("remote client connect...accepted");
3111 }
3112 else {
3113 close(fd);
3114 debug("another remote client connect...disconnected");
3115 }
3116 }
3117 else {
3118 /* Client disconnected */
3119 debug("remote client disconnected");
918cebb3 3120 close(rodent.mremcfd);
984263bc
MD
3121 rodent.mremcfd = -1;
3122 }
3123}