ps2comm.c 19.9 KB
Newer Older
1 2 3 4 5 6
/*
 * Copyright  1997 C. Scott Ananian
 * Copyright  1998-2000 Bruce Kalk
 * Copyright  2001 Stefan Gmeiner
 * Copyright  2002 Linuxcare Inc. David Kennedy
 * Copyright  2003 Fred Hucht <fred@thp.Uni-Duisburg.de>
7
 *
8 9 10 11 12 13 14 15 16 17
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Red Hat
 * not be used in advertising or publicity pertaining to distribution
 * of the software without specific, written prior permission.  Red
 * Hat makes no representations about the suitability of this software
 * for any purpose.  It is provided "as is" without express or implied
 * warranty.
18
 *
19 20 21 22 23 24 25
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26
 *
27 28 29 30 31 32
 * Authors:
 *      Stefan Gmeiner (riddlebox@freesurf.ch)
 *      C. Scott Ananian (cananian@alumni.priceton.edu)
 *      Bruce Kalk (kall@compass.com)
 *      Linuxcare Inc. David Kennedy (dkennedy@linuxcare.com)
 *      Fred Hucht (fred@thp.Uni-Duisburg.de)
33 34
 */

35 36 37 38
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

39
#include <xorg-server.h>
40
#include "ps2comm.h"
41
#include "synproto.h"
42
#include "synaptics.h"
43
#include <xf86.h>
44

45 46
#define MAX_UNSYNC_PACKETS 10				/* i.e. 10 to 60 bytes */

47
/* synaptics queries */
48 49 50 51 52 53 54 55
#define SYN_QUE_IDENTIFY		0x00
#define SYN_QUE_MODES			0x01
#define SYN_QUE_CAPABILITIES		0x02
#define SYN_QUE_MODEL			0x03
#define SYN_QUE_SERIAL_NUMBER_PREFIX	0x06
#define SYN_QUE_SERIAL_NUMBER_SUFFIX	0x07
#define SYN_QUE_RESOLUTION		0x08
#define SYN_QUE_EXT_CAPAB		0x09
56 57

/* status request response bits (PS2_CMD_STATUS_REQUEST) */
58 59 60 61 62 63 64 65
#define PS2_RES_REMOTE(r)	((r) & (1 << 22))
#define PS2_RES_ENABLE(r)	((r) & (1 << 21))
#define PS2_RES_SCALING(r)	((r) & (1 << 20))
#define PS2_RES_LEFT(r)		((r) & (1 << 18))
#define PS2_RES_MIDDLE(r)	((r) & (1 << 17))
#define PS2_RES_RIGHT(r)	((r) & (1 << 16))
#define PS2_RES_RESOLUTION(r)	(((r) >> 8) & 0x03)
#define PS2_RES_SAMPLE_RATE(r)	((r) & 0xff)
66

67
/* #define DEBUG */
68

69
#ifdef DEBUG
70
#define PS2DBG(x) (x)
71
#else
72
#define PS2DBG(x)
73
#endif
74

75 76 77 78
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 1
#define DBG(a,b)
#endif

79 80 81 82 83
/*****************************************************************************
 *	PS/2 Utility functions.
 *     Many parts adapted from tpconfig.c by C. Scott Ananian
 ****************************************************************************/

84
/*
85 86
 * Read a byte from the ps/2 port
 */
87 88
static Bool
ps2_getbyte(int fd, byte *b)
89
{
90 91
    if (xf86WaitForInput(fd, 50000) > 0) {
	if (xf86ReadSerial(fd, b, 1) != 1) {
92
	    PS2DBG(ErrorF("ps2_getbyte: No byte read\n"));
93
	    return FALSE;
94
	}
95
	PS2DBG(ErrorF("ps2_getbyte: byte %02X read\n", *b));
96
	return TRUE;
97
    }
98
    PS2DBG(ErrorF("ps2_getbyte: timeout xf86WaitForInput\n"));
99
    return FALSE;
100 101 102
}

/*
Peter Osterlund's avatar
Peter Osterlund committed
103
 * Write a byte to the ps/2 port, wait for ACK
104
 */
105
Bool
106
ps2_putbyte(int fd, byte b)
107
{
108
    byte ack;
109

110
    if (xf86WriteSerial(fd, &b, 1) != 1) {
111
	PS2DBG(ErrorF("ps2_putbyte: error xf86WriteSerial\n"));
112
	return FALSE;
113
    }
114
    PS2DBG(ErrorF("ps2_putbyte: byte %02X send\n", b));
115
    /* wait for an ACK */
116 117
    if (!ps2_getbyte(fd, &ack)) {
	return FALSE;
118
    }
119
    if (ack != PS2_ACK) {
120
	PS2DBG(ErrorF("ps2_putbyte: wrong acknowledge 0x%02x\n", ack));
121
	return FALSE;
122
    }
123
    return TRUE;
124 125 126
}

/*
127 128
 * Use the Synaptics extended ps/2 syntax to write a special command byte. Needed by
 * ps2_send_cmd and ps2_set_mode.
129 130 131
 * special command: 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
 *                  is the command. A 0xF3 or 0xE9 must follow (see ps2_send_cmd, ps2_set_mode)
 */
132 133
static Bool
ps2_special_cmd(int fd, byte cmd)
134
{
135 136 137
    int i;

    /* initialize with 'inert' command */
138 139 140 141 142 143 144 145 146 147 148
    if (!ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1))
	return FALSE;

    /* send 4x 2-bits with set resolution command */
    for (i = 0; i < 4; i++) {
	if (!ps2_putbyte(fd, PS2_CMD_SET_RESOLUTION) ||
	    !ps2_putbyte(fd, (cmd >> 6) & 0x3))
	    return FALSE;
	cmd <<= 2;
    }
    return TRUE;
149 150 151 152
}

/*
 * Send a command to the synpatics touchpad by special commands
153
 */
154 155 156
static Bool
ps2_send_cmd(int fd, byte c)
{
157
    PS2DBG(ErrorF("send command: 0x%02X\n", c));
158 159
    return (ps2_special_cmd(fd, c) &&
	    ps2_putbyte(fd, PS2_CMD_STATUS_REQUEST));
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175
/*****************************************************************************
 *	Synaptics passthrough functions
 ****************************************************************************/

static Bool
ps2_getbyte_passthrough(int fd, byte *response)
{
    byte ack;
    int timeout_count;
#define MAX_RETRY_COUNT 30

    /* Getting a response back through the passthrough could take some time.
     * Spin a little for the first byte */
    for (timeout_count = 0;
176
	 !ps2_getbyte(fd, &ack) && (timeout_count <= MAX_RETRY_COUNT);
177 178 179 180
	 timeout_count++)
	;
    /* Do some sanity checking */
    if ((ack & 0xfc) != 0x84) {
181 182
	PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0x84 and got: %02x\n",
		      ack & 0xfc));
183
	return FALSE;
184 185 186 187 188 189
    }

    ps2_getbyte(fd, response);
    ps2_getbyte(fd, &ack);
    ps2_getbyte(fd, &ack);
    if ((ack & 0xcc) != 0xc4) {
190 191
	PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0xc4 and got: %02x\n",
		      ack & 0xcc));
192
	return FALSE;
193 194 195 196
    }
    ps2_getbyte(fd, &ack);
    ps2_getbyte(fd, &ack);

197
    return TRUE;
198 199 200 201 202 203 204 205 206 207 208 209 210
}

static Bool
ps2_putbyte_passthrough(int fd, byte c)
{
    byte ack;

    ps2_special_cmd(fd, c);
    ps2_putbyte(fd, 0xF3);
    ps2_putbyte(fd, 0x28);

    ps2_getbyte_passthrough(fd, &ack);
    if (ack != PS2_ACK) {
211
	PS2DBG(ErrorF("ps2_putbyte_passthrough: wrong acknowledge 0x%02x\n", ack));
212
	return FALSE;
213
    }
214
    return TRUE;
215 216
}

217 218 219 220 221 222 223
/*****************************************************************************
 *	Synaptics communications functions
 ****************************************************************************/

/*
 * Set the synaptics touchpad mode byte by special commands
 */
224
static Bool
225
ps2_synaptics_set_mode(int fd, byte mode)
226
{
227
    PS2DBG(ErrorF("set mode byte to: 0x%02X\n", mode));
228 229
    return (ps2_special_cmd(fd, mode) &&
	    ps2_putbyte(fd, PS2_CMD_SET_SAMPLE_RATE) &&
230
	    ps2_putbyte(fd, 0x14));
231
}
232 233 234

/*
 * reset the touchpad
235
 */
236
static Bool
237
ps2_synaptics_reset(int fd)
238
{
239
    byte r[2];
240

241
    xf86FlushInput(fd);
242
    PS2DBG(ErrorF("Reset the Touchpad...\n"));
243
    if (!ps2_putbyte(fd, PS2_CMD_RESET)) {
244
	PS2DBG(ErrorF("...failed\n"));
245
	return FALSE;
246 247
    }
    xf86WaitForInput(fd, 4000000);
248
    if (ps2_getbyte(fd, &r[0]) && ps2_getbyte(fd, &r[1])) {
249
	if (r[0] == 0xAA && r[1] == 0x00) {
250
	    PS2DBG(ErrorF("...done\n"));
251
	    return TRUE;
252
	} else {
253
	    PS2DBG(ErrorF("...failed. Wrong reset ack 0x%02x, 0x%02x\n", r[0], r[1]));
254
	    return FALSE;
255
	}
256
    }
257
    PS2DBG(ErrorF("...failed\n"));
258
    return FALSE;
259 260
}

261
static Bool
262
ps2_synaptics_reset_passthrough(int fd)
263 264 265 266 267 268 269
{
    byte ack;

    /* send reset */
    ps2_putbyte_passthrough(fd, 0xff);
    ps2_getbyte_passthrough(fd, &ack);
    if (ack != 0xaa) {
270
	PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0xaa\n", ack));
271
	return FALSE;
272 273 274
    }
    ps2_getbyte_passthrough(fd, &ack);
    if (ack != 0x00) {
275
	PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0x00\n", ack));
276
	return FALSE;
277 278 279
    }

    /* set defaults, turn on streaming, and enable the mouse */
280 281
    return (ps2_putbyte_passthrough(fd, 0xf6) &&
	    ps2_putbyte_passthrough(fd, 0xea) &&
282 283 284
	    ps2_putbyte_passthrough(fd, 0xf4));
}

285
/*
286 287 288
 * Read the model-id bytes from the touchpad
 * see also SYN_MODEL_* macros
 */
289
static Bool
290
ps2_synaptics_model_id(int fd, struct SynapticsHwInfo *synhw)
291
{
292
    byte mi[3];
293

294
    PS2DBG(ErrorF("Read mode id...\n"));
295

296 297 298 299 300
    synhw->model_id = 0;
    if (ps2_send_cmd(fd, SYN_QUE_MODEL) &&
	ps2_getbyte(fd, &mi[0]) &&
	ps2_getbyte(fd, &mi[1]) &&
	ps2_getbyte(fd, &mi[2])) {
301
	synhw->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
302 303
	PS2DBG(ErrorF("mode-id %06X\n", synhw->model_id));
	PS2DBG(ErrorF("...done.\n"));
304
	return TRUE;
305
    }
306
    PS2DBG(ErrorF("...failed.\n"));
307
    return FALSE;
308 309 310 311 312 313
}

/*
 * Read the capability-bits from the touchpad
 * see also the SYN_CAP_* macros
 */
314
static Bool
315
ps2_synaptics_capability(int fd, struct SynapticsHwInfo *synhw)
316
{
317
    byte cap[3];
318

319
    PS2DBG(ErrorF("Read capabilites...\n"));
320

321
    synhw->capabilities = 0;
322
    synhw->ext_cap = 0;
323 324 325 326
    if (ps2_send_cmd(fd, SYN_QUE_CAPABILITIES) &&
	ps2_getbyte(fd, &cap[0]) &&
	ps2_getbyte(fd, &cap[1]) &&
	ps2_getbyte(fd, &cap[2])) {
327
	synhw->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
328
	PS2DBG(ErrorF("capabilities %06X\n", synhw->capabilities));
329 330
	if (SYN_CAP_VALID(*synhw)) {
	    if (SYN_EXT_CAP_REQUESTS(*synhw)) {
331 332 333 334
		if (ps2_send_cmd(fd, SYN_QUE_EXT_CAPAB) &&
		    ps2_getbyte(fd, &cap[0]) &&
		    ps2_getbyte(fd, &cap[1]) &&
		    ps2_getbyte(fd, &cap[2])) {
335
		    synhw->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
336
		    PS2DBG(ErrorF("ext-capability %06X\n", synhw->ext_cap));
337
		} else {
338 339
		    PS2DBG(ErrorF("synaptics says, that it has extended-capabilities, "
				  "but I cannot read them."));
340 341
		}
	    }
342
	    PS2DBG(ErrorF("...done.\n"));
343
	    return TRUE;
344
	}
345
    }
346
    PS2DBG(ErrorF("...failed.\n"));
347
    return FALSE;
348 349 350
}

/*
351
 * Identify Touchpad
352 353
 * See also the SYN_ID_* macros
 */
354
static Bool
355
ps2_synaptics_identify(int fd, struct SynapticsHwInfo *synhw)
356
{
357
    byte id[3];
358

359
    PS2DBG(ErrorF("Identify Touchpad...\n"));
360

361
    synhw->identity = 0;
362 363 364 365
    if (ps2_send_cmd(fd, SYN_QUE_IDENTIFY) &&
	ps2_getbyte(fd, &id[0]) &&
	ps2_getbyte(fd, &id[1]) &&
	ps2_getbyte(fd, &id[2])) {
366
	synhw->identity = (id[0] << 16) | (id[1] << 8) | id[2];
367
	PS2DBG(ErrorF("ident %06X\n", synhw->identity));
368
	if (SYN_ID_IS_SYNAPTICS(*synhw)) {
369
	    PS2DBG(ErrorF("...done.\n"));
370
	    return TRUE;
371
	}
372
    }
373
    PS2DBG(ErrorF("...failed.\n"));
374
    return FALSE;
375 376
}

377
static Bool
378
ps2_synaptics_enable_device(int fd)
379
{
380
    return ps2_putbyte(fd, PS2_CMD_ENABLE);
381 382
}

383
static Bool
384
ps2_synaptics_disable_device(int fd)
385
{
386 387
    xf86FlushInput(fd);
    return ps2_putbyte(fd, PS2_CMD_DISABLE);
388 389
}

390
static Bool
391
ps2_query_is_synaptics(int fd)
392
{
393
    struct SynapticsHwInfo synhw;
394 395 396
    int i;

    for (i = 0; i < 3; i++) {
397
	if (ps2_synaptics_disable_device(fd))
398 399 400 401 402
	    break;
    }

    xf86WaitForInput(fd, 20000);
    xf86FlushInput(fd);
403
    if (ps2_synaptics_identify(fd, &synhw)) {
404 405
	return TRUE;
    } else {
406
	ErrorF("Query no Synaptics: %06X\n", synhw.identity);
407 408
	return FALSE;
    }
409
}
410

411
void
412
ps2_print_ident(const struct SynapticsHwInfo *synhw)
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
{
    xf86Msg(X_PROBED, " Synaptics Touchpad, model: %d\n", SYN_ID_MODEL(*synhw));
    xf86Msg(X_PROBED, " Firmware: %d.%d\n", SYN_ID_MAJOR(*synhw),
	    SYN_ID_MINOR(*synhw));

    if (SYN_MODEL_ROT180(*synhw))
	xf86Msg(X_PROBED, " 180 degree mounted touchpad\n");
    if (SYN_MODEL_PORTRAIT(*synhw))
	xf86Msg(X_PROBED, " portrait touchpad\n");
    xf86Msg(X_PROBED, " Sensor: %d\n", SYN_MODEL_SENSOR(*synhw));
    if (SYN_MODEL_NEWABS(*synhw))
	xf86Msg(X_PROBED, " new absolute packet format\n");
    if (SYN_MODEL_PEN(*synhw))
	xf86Msg(X_PROBED, " pen detection\n");

    if (SYN_CAP_EXTENDED(*synhw)) {
	xf86Msg(X_PROBED, " Touchpad has extended capability bits\n");
	if (SYN_CAP_MULTI_BUTTON_NO(*synhw))
	    xf86Msg(X_PROBED, " -> %d multi buttons, i.e. besides standard buttons\n",
		    (int)(SYN_CAP_MULTI_BUTTON_NO(*synhw)));
433 434 435
	if (SYN_CAP_MIDDLE_BUTTON(*synhw))
	    xf86Msg(X_PROBED, " -> middle button\n");
	if (SYN_CAP_FOUR_BUTTON(*synhw))
436 437 438 439 440 441 442 443 444 445
	    xf86Msg(X_PROBED, " -> four buttons\n");
	if (SYN_CAP_MULTIFINGER(*synhw))
	    xf86Msg(X_PROBED, " -> multifinger detection\n");
	if (SYN_CAP_PALMDETECT(*synhw))
	    xf86Msg(X_PROBED, " -> palm detection\n");
	if (SYN_CAP_PASSTHROUGH(*synhw))
	    xf86Msg(X_PROBED, " -> pass-through port\n");
    }
}

446 447

static void
448
PS2DeviceOnHook(LocalDevicePtr local, SynapticsSHM* para)
449 450 451 452
{
}

static void
453
PS2DeviceOffHook(LocalDevicePtr local)
454
{
455 456
    ps2_synaptics_reset(local->fd);
    ps2_synaptics_enable_device(local->fd);
457 458
}

459
static Bool
460
PS2QueryHardware(LocalDevicePtr local, struct SynapticsHwInfo *synhw)
461 462 463 464
{
    int mode;

    /* is the synaptics touchpad active? */
465
    if (!ps2_query_is_synaptics(local->fd))
466 467 468 469
	return FALSE;

    xf86Msg(X_PROBED, "%s synaptics touchpad found\n", local->name);

470
    if (!ps2_synaptics_reset(local->fd))
471 472
	xf86Msg(X_ERROR, "%s reset failed\n", local->name);

473
    if (!ps2_synaptics_identify(local->fd, synhw))
474 475
	return FALSE;

476
    if (!ps2_synaptics_model_id(local->fd, synhw))
477 478
	return FALSE;

479
    if (!ps2_synaptics_capability(local->fd, synhw))
480 481 482 483 484 485 486
	return FALSE;

    mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
    if (SYN_ID_MAJOR(*synhw) >= 4)
	mode |= SYN_BIT_DISABLE_GESTURE;
    if (SYN_CAP_EXTENDED(*synhw))
	mode |= SYN_BIT_W_MODE;
487
    if (!ps2_synaptics_set_mode(local->fd, mode))
488 489 490
	return FALSE;

    /* Check to see if the host mouse supports a guest */
491
    synhw->hasGuest = FALSE;
492
    if (SYN_CAP_PASSTHROUGH(*synhw)) {
493
        synhw->hasGuest = TRUE;
494 495 496 497 498

	/* Enable the guest mouse.  Set it to relative mode, three byte
	 * packets */

	/* Disable the host to talk to the guest */
499
	ps2_synaptics_disable_device(local->fd);
500
	/* Reset it, set defaults, streaming and enable it */
501
	if (!ps2_synaptics_reset_passthrough(local->fd)) {
502
	    synhw->hasGuest = FALSE;
503 504 505
	}
    }

506
    ps2_synaptics_enable_device(local->fd);
507

508
    ps2_print_ident(synhw);
509 510 511 512

    return TRUE;
}

513 514 515 516
/*
 * Decide if the current packet stored in priv->protoBuf is valid.
 */
static Bool
517
ps2_packet_ok(struct SynapticsHwInfo *synhw, struct CommData *comm)
518
{
519 520
    unsigned char *buf = comm->protoBuf;
    int newabs = SYN_MODEL_NEWABS(*synhw);
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545

    if (newabs ? ((buf[0] & 0xC0) != 0x80) : ((buf[0] & 0xC0) != 0xC0)) {
	DBG(4, ErrorF("Synaptics driver lost sync at 1st byte\n"));
	return FALSE;
    }

    if (!newabs && ((buf[1] & 0x60) != 0x00)) {
	DBG(4, ErrorF("Synaptics driver lost sync at 2nd byte\n"));
	return FALSE;
    }

    if ((newabs ? ((buf[3] & 0xC0) != 0xC0) : ((buf[3] & 0xC0) != 0x80))) {
	DBG(4, ErrorF("Synaptics driver lost sync at 4th byte\n"));
	return FALSE;
    }

    if (!newabs && ((buf[4] & 0x60) != 0x00)) {
	DBG(4, ErrorF("Synaptics driver lost sync at 5th byte\n"));
	return FALSE;
    }

    return TRUE;
}

static Bool
546
ps2_synaptics_get_packet(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
547
			 struct SynapticsProtocolOperations *proto_ops,
548
			 struct CommData *comm)
549 550 551 552 553
{
    int count = 0;
    int c;
    unsigned char u;

554
    while ((c = XisbRead(comm->buffer)) >= 0) {
555 556 557
	u = (unsigned char)c;

	/* test if there is a reset sequence received */
558
	if ((c == 0x00) && (comm->lastByte == 0xAA)) {
559 560
	    if (xf86WaitForInput(local->fd, 50000) == 0) {
		DBG(7, ErrorF("Reset received\n"));
561
		proto_ops->QueryHardware(local, synhw);
562 563 564
	    } else
		DBG(3, ErrorF("faked reset received\n"));
	}
565
	comm->lastByte = u;
566 567 568 569 570 571 572

	/* to avoid endless loops */
	if (count++ > 30) {
	    ErrorF("Synaptics driver lost sync... got gigantic packet!\n");
	    return FALSE;
	}

573
	comm->protoBuf[comm->protoBufTail++] = u;
574 575 576

	/* Check that we have a valid packet. If not, we are out of sync,
	   so we throw away the first byte in the packet.*/
577
	if (comm->protoBufTail >= 6) {
578
	    if (!ps2_packet_ok(synhw, comm)) {
579
		int i;
580 581 582 583 584 585
		for (i = 0; i < comm->protoBufTail - 1; i++)
		    comm->protoBuf[i] = comm->protoBuf[i + 1];
		comm->protoBufTail--;
		comm->outOfSync++;
		if (comm->outOfSync > MAX_UNSYNC_PACKETS) {
		    comm->outOfSync = 0;
586
		    DBG(3, ErrorF("Synaptics synchronization lost too long -> reset touchpad.\n"));
587
		    proto_ops->QueryHardware(local, synhw); /* including a reset */
588 589 590 591 592
		    continue;
		}
	    }
	}

593 594 595
	if (comm->protoBufTail >= 6) { /* Full packet received */
	    if (comm->outOfSync > 0) {
		comm->outOfSync = 0;
596 597
		DBG(4, ErrorF("Synaptics driver resynced.\n"));
	    }
598
	    comm->protoBufTail = 0;
599 600 601 602 603 604 605 606
	    return TRUE;
	}
    }

    return FALSE;
}

static Bool
607
PS2ReadHwState(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
608
	       struct SynapticsProtocolOperations *proto_ops,
609
	       struct CommData *comm, struct SynapticsHwState *hwRet)
610
{
611 612 613
    int newabs = SYN_MODEL_NEWABS(*synhw);
    unsigned char *buf = comm->protoBuf;
    struct SynapticsHwState *hw = &(comm->hwState);
614 615
    int w, i;

616
    if (!ps2_synaptics_get_packet(local, synhw, proto_ops, comm))
617 618 619 620
	return FALSE;

    /* Handle guest packets */
    hw->guest_dx = hw->guest_dy = 0;
621
    if (newabs && synhw->hasGuest) {
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
	w = (((buf[0] & 0x30) >> 2) |
	     ((buf[0] & 0x04) >> 1) |
	     ((buf[3] & 0x04) >> 2));
	if (w == 3) {	       /* If w is 3, this is a guest packet */
	    if (buf[4] != 0)
		hw->guest_dx =   buf[4] - ((buf[1] & 0x10) ? 256 : 0);
	    if (buf[5] != 0)
		hw->guest_dy = -(buf[5] - ((buf[1] & 0x20) ? 256 : 0));
	    hw->guest_left  = (buf[1] & 0x01) ? TRUE : FALSE;
	    hw->guest_mid   = (buf[1] & 0x04) ? TRUE : FALSE;
	    hw->guest_right = (buf[1] & 0x02) ? TRUE : FALSE;
	    *hwRet = *hw;
	    return TRUE;
	}
    }

    /* Handle normal packets */
    hw->x = hw->y = hw->z = hw->numFingers = hw->fingerWidth = 0;
    hw->left = hw->right = hw->up = hw->down = hw->middle = FALSE;
    for (i = 0; i < 8; i++)
	hw->multi[i] = FALSE;

    if (newabs) {			    /* newer protos...*/
	DBG(7, ErrorF("using new protocols\n"));
	hw->x = (((buf[3] & 0x10) << 8) |
		 ((buf[1] & 0x0f) << 8) |
		 buf[4]);
	hw->y = (((buf[3] & 0x20) << 7) |
		 ((buf[1] & 0xf0) << 4) |
		 buf[5]);

	hw->z = buf[2];
	w = (((buf[0] & 0x30) >> 2) |
	     ((buf[0] & 0x04) >> 1) |
	     ((buf[3] & 0x04) >> 2));

	hw->left  = (buf[0] & 0x01) ? 1 : 0;
	hw->right = (buf[0] & 0x02) ? 1 : 0;

661
	if (SYN_CAP_EXTENDED(*synhw)) {
662 663 664
	    if (SYN_CAP_MIDDLE_BUTTON(*synhw)) {
		hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
	    }
665
	    if (SYN_CAP_FOUR_BUTTON(*synhw)) {
666 667 668 669 670 671 672
		hw->up = ((buf[3] & 0x01)) ? 1 : 0;
		if (hw->left)
		    hw->up = !hw->up;
		hw->down = ((buf[3] & 0x02)) ? 1 : 0;
		if (hw->right)
		    hw->down = !hw->down;
	    }
673
	    if (SYN_CAP_MULTI_BUTTON_NO(*synhw)) {
674
		if ((buf[3] & 2) ? !hw->right : hw->right) {
675
		    switch (SYN_CAP_MULTI_BUTTON_NO(*synhw) & ~0x01) {
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
		    default:
			break;
		    case 8:
			hw->multi[7] = ((buf[5] & 0x08)) ? 1 : 0;
			hw->multi[6] = ((buf[4] & 0x08)) ? 1 : 0;
		    case 6:
			hw->multi[5] = ((buf[5] & 0x04)) ? 1 : 0;
			hw->multi[4] = ((buf[4] & 0x04)) ? 1 : 0;
		    case 4:
			hw->multi[3] = ((buf[5] & 0x02)) ? 1 : 0;
			hw->multi[2] = ((buf[4] & 0x02)) ? 1 : 0;
		    case 2:
			hw->multi[1] = ((buf[5] & 0x01)) ? 1 : 0;
			hw->multi[0] = ((buf[4] & 0x01)) ? 1 : 0;
		    }
		}
	    }
	}
    } else {			    /* old proto...*/
	DBG(7, ErrorF("using old protocol\n"));
	hw->x = (((buf[1] & 0x1F) << 8) |
		 buf[2]);
	hw->y = (((buf[4] & 0x1F) << 8) |
		 buf[5]);

	hw->z = (((buf[0] & 0x30) << 2) |
		 (buf[3] & 0x3F));
	w = (((buf[1] & 0x80) >> 4) |
	     ((buf[0] & 0x04) >> 1));

	hw->left  = (buf[0] & 0x01) ? 1 : 0;
	hw->right = (buf[0] & 0x02) ? 1 : 0;
    }

    hw->y = YMAX_NOMINAL + YMIN_NOMINAL - hw->y;

    if (hw->z > 0) {
	int w_ok = 0;
	/*
	 * Use capability bits to decide if the w value is valid.
	 * If not, set it to 5, which corresponds to a finger of
	 * normal width.
	 */
719
	if (SYN_CAP_EXTENDED(*synhw)) {
720
	    if ((w >= 0) && (w <= 1)) {
721
		w_ok = SYN_CAP_MULTIFINGER(*synhw);
722
	    } else if (w == 2) {
723
		w_ok = SYN_MODEL_PEN(*synhw);
724
	    } else if ((w >= 4) && (w <= 15)) {
725
		w_ok = SYN_CAP_PALMDETECT(*synhw);
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
	    }
	}
	if (!w_ok)
	    w = 5;

	switch (w) {
	case 0:
	    hw->numFingers = 2;
	    hw->fingerWidth = 5;
	    break;
	case 1:
	    hw->numFingers = 3;
	    hw->fingerWidth = 5;
	    break;
	default:
	    hw->numFingers = 1;
	    hw->fingerWidth = w;
	    break;
	}
    }

    *hwRet = *hw;
    return TRUE;
}

751 752
static Bool
PS2AutoDevProbe(LocalDevicePtr local)
753 754 755 756
{
    return FALSE;
}

757
struct SynapticsProtocolOperations psaux_proto_operations = {
758 759
    PS2DeviceOnHook,
    PS2DeviceOffHook,
760
    PS2QueryHardware,
761
    PS2ReadHwState,
762
    PS2AutoDevProbe,
763
    NULL /* ReadDevDimensions */
764
};