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