ps2comm.c 19.5 KB
Newer Older
1
/* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
2
 *
3 4 5 6
 *   Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
 *   Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
 *     code fr the special synaptics commands (from the tpconfig-source)
 *
7 8 9 10 11
 *   Synaptics Passthrough Support
 *   Copyright (c) 2002 Linuxcare Inc. David Kennedy <dkennedy@linuxcare.com>
 *   adapted to version 0.12.1
 *   Copyright (c) 2003 Fred Hucht <fred@thp.Uni-Duisburg.de>
 *
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

28 29 30 31
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

32
#include "ps2comm.h"
33
#include "synproto.h"
34
#include "synaptics.h"
35
#include <xf86.h>
36

37 38
#define MAX_UNSYNC_PACKETS 10				/* i.e. 10 to 60 bytes */

39
/* synaptics queries */
40 41 42 43 44 45 46 47
#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
48 49

/* status request response bits (PS2_CMD_STATUS_REQUEST) */
50 51 52 53 54 55 56 57
#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)
58

59
/* #define DEBUG */
60

61
#ifdef DEBUG
62
#define PS2DBG(x) (x)
63
#else
64
#define PS2DBG(x)
65
#endif
66

67 68 69 70
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 1
#define DBG(a,b)
#endif

71 72 73 74 75
/*****************************************************************************
 *	PS/2 Utility functions.
 *     Many parts adapted from tpconfig.c by C. Scott Ananian
 ****************************************************************************/

76
/*
77 78
 * Read a byte from the ps/2 port
 */
79 80
static Bool
ps2_getbyte(int fd, byte *b)
81
{
82 83
    if (xf86WaitForInput(fd, 50000) > 0) {
	if (xf86ReadSerial(fd, b, 1) != 1) {
84
	    PS2DBG(ErrorF("ps2_getbyte: No byte read\n"));
85
	    return FALSE;
86
	}
87
	PS2DBG(ErrorF("ps2_getbyte: byte %02X read\n", *b));
88
	return TRUE;
89
    }
90
    PS2DBG(ErrorF("ps2_getbyte: timeout xf86WaitForInput\n"));
91
    return FALSE;
92 93 94
}

/*
Peter Osterlund's avatar
Peter Osterlund committed
95
 * Write a byte to the ps/2 port, wait for ACK
96
 */
97
Bool
98
ps2_putbyte(int fd, byte b)
99
{
100
    byte ack;
101

102
    if (xf86WriteSerial(fd, &b, 1) != 1) {
103
	PS2DBG(ErrorF("ps2_putbyte: error xf86WriteSerial\n"));
104
	return FALSE;
105
    }
106
    PS2DBG(ErrorF("ps2_putbyte: byte %02X send\n", b));
107
    /* wait for an ACK */
108 109
    if (!ps2_getbyte(fd, &ack)) {
	return FALSE;
110
    }
111
    if (ack != PS2_ACK) {
112
	PS2DBG(ErrorF("ps2_putbyte: wrong acknowledge 0x%02x\n", ack));
113
	return FALSE;
114
    }
115
    return TRUE;
116 117 118
}

/*
119 120
 * Use the Synaptics extended ps/2 syntax to write a special command byte. Needed by
 * ps2_send_cmd and ps2_set_mode.
121 122 123
 * 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)
 */
124 125
static Bool
ps2_special_cmd(int fd, byte cmd)
126
{
127 128 129
    int i;

    /* initialize with 'inert' command */
130 131 132 133 134 135 136 137 138 139 140
    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;
141 142 143 144
}

/*
 * Send a command to the synpatics touchpad by special commands
145
 */
146 147 148
static Bool
ps2_send_cmd(int fd, byte c)
{
149
    PS2DBG(ErrorF("send command: 0x%02X\n", c));
150 151
    return (ps2_special_cmd(fd, c) &&
	    ps2_putbyte(fd, PS2_CMD_STATUS_REQUEST));
152 153
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167
/*****************************************************************************
 *	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;
168
	 !ps2_getbyte(fd, &ack) && (timeout_count <= MAX_RETRY_COUNT);
169 170 171 172
	 timeout_count++)
	;
    /* Do some sanity checking */
    if ((ack & 0xfc) != 0x84) {
173 174
	PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0x84 and got: %02x\n",
		      ack & 0xfc));
175
	return FALSE;
176 177 178 179 180 181
    }

    ps2_getbyte(fd, response);
    ps2_getbyte(fd, &ack);
    ps2_getbyte(fd, &ack);
    if ((ack & 0xcc) != 0xc4) {
182 183
	PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0xc4 and got: %02x\n",
		      ack & 0xcc));
184
	return FALSE;
185 186 187 188
    }
    ps2_getbyte(fd, &ack);
    ps2_getbyte(fd, &ack);

189
    return TRUE;
190 191 192 193 194 195 196 197 198 199 200 201 202
}

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) {
203
	PS2DBG(ErrorF("ps2_putbyte_passthrough: wrong acknowledge 0x%02x\n", ack));
204
	return FALSE;
205
    }
206
    return TRUE;
207 208
}

209 210 211 212 213 214 215
/*****************************************************************************
 *	Synaptics communications functions
 ****************************************************************************/

/*
 * Set the synaptics touchpad mode byte by special commands
 */
216
static Bool
217
ps2_synaptics_set_mode(int fd, byte mode)
218
{
219
    PS2DBG(ErrorF("set mode byte to: 0x%02X\n", mode));
220 221
    return (ps2_special_cmd(fd, mode) &&
	    ps2_putbyte(fd, PS2_CMD_SET_SAMPLE_RATE) &&
222
	    ps2_putbyte(fd, 0x14));
223
}
224 225 226

/*
 * reset the touchpad
227
 */
228
static Bool
229
ps2_synaptics_reset(int fd)
230
{
231
    byte r[2];
232

233
    xf86FlushInput(fd);
234
    PS2DBG(ErrorF("Reset the Touchpad...\n"));
235
    if (!ps2_putbyte(fd, PS2_CMD_RESET)) {
236
	PS2DBG(ErrorF("...failed\n"));
237
	return FALSE;
238 239
    }
    xf86WaitForInput(fd, 4000000);
240
    if (ps2_getbyte(fd, &r[0]) && ps2_getbyte(fd, &r[1])) {
241
	if (r[0] == 0xAA && r[1] == 0x00) {
242
	    PS2DBG(ErrorF("...done\n"));
243
	    return TRUE;
244
	} else {
245
	    PS2DBG(ErrorF("...failed. Wrong reset ack 0x%02x, 0x%02x\n", r[0], r[1]));
246
	    return FALSE;
247
	}
248
    }
249
    PS2DBG(ErrorF("...failed\n"));
250
    return FALSE;
251 252
}

253
static Bool
254
ps2_synaptics_reset_passthrough(int fd)
255 256 257 258 259 260 261
{
    byte ack;

    /* send reset */
    ps2_putbyte_passthrough(fd, 0xff);
    ps2_getbyte_passthrough(fd, &ack);
    if (ack != 0xaa) {
262
	PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0xaa\n", ack));
263
	return FALSE;
264 265 266
    }
    ps2_getbyte_passthrough(fd, &ack);
    if (ack != 0x00) {
267
	PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0x00\n", ack));
268
	return FALSE;
269 270 271
    }

    /* set defaults, turn on streaming, and enable the mouse */
272 273
    return (ps2_putbyte_passthrough(fd, 0xf6) &&
	    ps2_putbyte_passthrough(fd, 0xea) &&
274 275 276
	    ps2_putbyte_passthrough(fd, 0xf4));
}

277
/*
278 279 280
 * Read the model-id bytes from the touchpad
 * see also SYN_MODEL_* macros
 */
281
static Bool
282
ps2_synaptics_model_id(int fd, struct SynapticsHwInfo *synhw)
283
{
284
    byte mi[3];
285

286
    PS2DBG(ErrorF("Read mode id...\n"));
287

288 289 290 291 292
    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])) {
293
	synhw->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
294 295
	PS2DBG(ErrorF("mode-id %06X\n", synhw->model_id));
	PS2DBG(ErrorF("...done.\n"));
296
	return TRUE;
297
    }
298
    PS2DBG(ErrorF("...failed.\n"));
299
    return FALSE;
300 301 302 303 304 305
}

/*
 * Read the capability-bits from the touchpad
 * see also the SYN_CAP_* macros
 */
306
static Bool
307
ps2_synaptics_capability(int fd, struct SynapticsHwInfo *synhw)
308
{
309
    byte cap[3];
310

311
    PS2DBG(ErrorF("Read capabilites...\n"));
312

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

/*
343
 * Identify Touchpad
344 345
 * See also the SYN_ID_* macros
 */
346
static Bool
347
ps2_synaptics_identify(int fd, struct SynapticsHwInfo *synhw)
348
{
349
    byte id[3];
350

351
    PS2DBG(ErrorF("Identify Touchpad...\n"));
352

353
    synhw->identity = 0;
354 355 356 357
    if (ps2_send_cmd(fd, SYN_QUE_IDENTIFY) &&
	ps2_getbyte(fd, &id[0]) &&
	ps2_getbyte(fd, &id[1]) &&
	ps2_getbyte(fd, &id[2])) {
358
	synhw->identity = (id[0] << 16) | (id[1] << 8) | id[2];
359
	PS2DBG(ErrorF("ident %06X\n", synhw->identity));
360
	if (SYN_ID_IS_SYNAPTICS(*synhw)) {
361
	    PS2DBG(ErrorF("...done.\n"));
362
	    return TRUE;
363
	}
364
    }
365
    PS2DBG(ErrorF("...failed.\n"));
366
    return FALSE;
367 368
}

369
static Bool
370
ps2_synaptics_enable_device(int fd)
371
{
372
    return ps2_putbyte(fd, PS2_CMD_ENABLE);
373 374
}

375
static Bool
376
ps2_synaptics_disable_device(int fd)
377
{
378 379
    xf86FlushInput(fd);
    return ps2_putbyte(fd, PS2_CMD_DISABLE);
380 381
}

382
static Bool
383
ps2_query_is_synaptics(int fd)
384
{
385
    struct SynapticsHwInfo synhw;
386 387 388
    int i;

    for (i = 0; i < 3; i++) {
389
	if (ps2_synaptics_disable_device(fd))
390 391 392 393 394
	    break;
    }

    xf86WaitForInput(fd, 20000);
    xf86FlushInput(fd);
395
    if (ps2_synaptics_identify(fd, &synhw)) {
396 397
	return TRUE;
    } else {
398
	ErrorF("Query no Synaptics: %06X\n", synhw.identity);
399 400
	return FALSE;
    }
401
}
402

403
void
404
ps2_print_ident(const struct SynapticsHwInfo *synhw)
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
{
    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)));
425 426 427
	if (SYN_CAP_MIDDLE_BUTTON(*synhw))
	    xf86Msg(X_PROBED, " -> middle button\n");
	if (SYN_CAP_FOUR_BUTTON(*synhw))
428 429 430 431 432 433 434 435 436 437
	    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");
    }
}

438 439

static void
440
PS2DeviceOnHook(LocalDevicePtr local, SynapticsSHM* para)
441 442 443 444
{
}

static void
445
PS2DeviceOffHook(LocalDevicePtr local)
446
{
447 448
    ps2_synaptics_reset(local->fd);
    ps2_synaptics_enable_device(local->fd);
449 450
}

451
static Bool
452
PS2QueryHardware(LocalDevicePtr local, struct SynapticsHwInfo *synhw)
453 454 455 456
{
    int mode;

    /* is the synaptics touchpad active? */
457
    if (!ps2_query_is_synaptics(local->fd))
458 459 460 461
	return FALSE;

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

462
    if (!ps2_synaptics_reset(local->fd))
463 464
	xf86Msg(X_ERROR, "%s reset failed\n", local->name);

465
    if (!ps2_synaptics_identify(local->fd, synhw))
466 467
	return FALSE;

468
    if (!ps2_synaptics_model_id(local->fd, synhw))
469 470
	return FALSE;

471
    if (!ps2_synaptics_capability(local->fd, synhw))
472 473 474 475 476 477 478
	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;
479
    if (!ps2_synaptics_set_mode(local->fd, mode))
480 481 482
	return FALSE;

    /* Check to see if the host mouse supports a guest */
483
    synhw->hasGuest = FALSE;
484
    if (SYN_CAP_PASSTHROUGH(*synhw)) {
485
        synhw->hasGuest = TRUE;
486 487 488 489 490

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

	/* Disable the host to talk to the guest */
491
	ps2_synaptics_disable_device(local->fd);
492
	/* Reset it, set defaults, streaming and enable it */
493
	if (!ps2_synaptics_reset_passthrough(local->fd)) {
494
	    synhw->hasGuest = FALSE;
495 496 497
	}
    }

498
    ps2_synaptics_enable_device(local->fd);
499

500
    ps2_print_ident(synhw);
501 502 503 504

    return TRUE;
}

505 506 507 508
/*
 * Decide if the current packet stored in priv->protoBuf is valid.
 */
static Bool
509
ps2_packet_ok(struct SynapticsHwInfo *synhw, struct CommData *comm)
510
{
511 512
    unsigned char *buf = comm->protoBuf;
    int newabs = SYN_MODEL_NEWABS(*synhw);
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537

    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
538
ps2_synaptics_get_packet(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
539
			 struct SynapticsProtocolOperations *proto_ops,
540
			 struct CommData *comm)
541 542 543 544 545
{
    int count = 0;
    int c;
    unsigned char u;

546
    while ((c = XisbRead(comm->buffer)) >= 0) {
547 548 549
	u = (unsigned char)c;

	/* test if there is a reset sequence received */
550
	if ((c == 0x00) && (comm->lastByte == 0xAA)) {
551 552
	    if (xf86WaitForInput(local->fd, 50000) == 0) {
		DBG(7, ErrorF("Reset received\n"));
553
		proto_ops->QueryHardware(local, synhw);
554 555 556
	    } else
		DBG(3, ErrorF("faked reset received\n"));
	}
557
	comm->lastByte = u;
558 559 560 561 562 563 564

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

565
	comm->protoBuf[comm->protoBufTail++] = u;
566 567 568

	/* Check that we have a valid packet. If not, we are out of sync,
	   so we throw away the first byte in the packet.*/
569
	if (comm->protoBufTail >= 6) {
570
	    if (!ps2_packet_ok(synhw, comm)) {
571
		int i;
572 573 574 575 576 577
		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;
578
		    DBG(3, ErrorF("Synaptics synchronization lost too long -> reset touchpad.\n"));
579
		    proto_ops->QueryHardware(local, synhw); /* including a reset */
580 581 582 583 584
		    continue;
		}
	    }
	}

585 586 587
	if (comm->protoBufTail >= 6) { /* Full packet received */
	    if (comm->outOfSync > 0) {
		comm->outOfSync = 0;
588 589
		DBG(4, ErrorF("Synaptics driver resynced.\n"));
	    }
590
	    comm->protoBufTail = 0;
591 592 593 594 595 596 597 598
	    return TRUE;
	}
    }

    return FALSE;
}

static Bool
599
PS2ReadHwState(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
600
	       struct SynapticsProtocolOperations *proto_ops,
601
	       struct CommData *comm, struct SynapticsHwState *hwRet)
602
{
603 604 605
    int newabs = SYN_MODEL_NEWABS(*synhw);
    unsigned char *buf = comm->protoBuf;
    struct SynapticsHwState *hw = &(comm->hwState);
606 607
    int w, i;

608
    if (!ps2_synaptics_get_packet(local, synhw, proto_ops, comm))
609 610 611 612
	return FALSE;

    /* Handle guest packets */
    hw->guest_dx = hw->guest_dy = 0;
613
    if (newabs && synhw->hasGuest) {
614 615 616 617 618 619 620 621 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
	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;

653
	if (SYN_CAP_EXTENDED(*synhw)) {
654 655 656
	    if (SYN_CAP_MIDDLE_BUTTON(*synhw)) {
		hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
	    }
657
	    if (SYN_CAP_FOUR_BUTTON(*synhw)) {
658 659 660 661 662 663 664
		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;
	    }
665
	    if (SYN_CAP_MULTI_BUTTON_NO(*synhw)) {
666
		if ((buf[3] & 2) ? !hw->right : hw->right) {
667
		    switch (SYN_CAP_MULTI_BUTTON_NO(*synhw) & ~0x01) {
668 669 670 671 672 673 674 675 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
		    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.
	 */
711
	if (SYN_CAP_EXTENDED(*synhw)) {
712
	    if ((w >= 0) && (w <= 1)) {
713
		w_ok = SYN_CAP_MULTIFINGER(*synhw);
714
	    } else if (w == 2) {
715
		w_ok = SYN_MODEL_PEN(*synhw);
716
	    } else if ((w >= 4) && (w <= 15)) {
717
		w_ok = SYN_CAP_PALMDETECT(*synhw);
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
	    }
	}
	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;
}

743 744
static Bool
PS2AutoDevProbe(LocalDevicePtr local)
745 746 747 748
{
    return FALSE;
}

749
struct SynapticsProtocolOperations psaux_proto_operations = {
750 751
    PS2DeviceOnHook,
    PS2DeviceOffHook,
752
    PS2QueryHardware,
753 754
    PS2ReadHwState,
    PS2AutoDevProbe
755
};