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