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