ps2comm.c 20.7 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 "synapticsstr.h"
44
#include <xf86.h>
45

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

48
/* synaptics queries */
49 50 51 52 53 54 55 56
#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
57 58

/* status request response bits (PS2_CMD_STATUS_REQUEST) */
59 60 61 62 63 64 65 66
#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)
67

68
/* #define DEBUG */
69

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

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

80 81 82 83 84 85 86 87
struct SynapticsHwInfo {
    unsigned int model_id;		    /* Model-ID */
    unsigned int capabilities;		    /* Capabilities */
    unsigned int ext_cap;		    /* Extended Capabilities */
    unsigned int identity;		    /* Identification */
    Bool hasGuest;			    /* Has a guest mouse */
};

88 89 90 91 92
/*****************************************************************************
 *	PS/2 Utility functions.
 *     Many parts adapted from tpconfig.c by C. Scott Ananian
 ****************************************************************************/

93
/*
94 95
 * Read a byte from the ps/2 port
 */
96 97
static Bool
ps2_getbyte(int fd, byte *b)
98
{
99 100
    if (xf86WaitForInput(fd, 50000) > 0) {
	if (xf86ReadSerial(fd, b, 1) != 1) {
101
	    PS2DBG(ErrorF("ps2_getbyte: No byte read\n"));
102
	    return FALSE;
103
	}
104
	PS2DBG(ErrorF("ps2_getbyte: byte %02X read\n", *b));
105
	return TRUE;
106
    }
107
    PS2DBG(ErrorF("ps2_getbyte: timeout xf86WaitForInput\n"));
108
    return FALSE;
109 110 111
}

/*
Peter Osterlund's avatar
Peter Osterlund committed
112
 * Write a byte to the ps/2 port, wait for ACK
113
 */
114
Bool
115
ps2_putbyte(int fd, byte b)
116
{
117
    byte ack;
118

119
    if (xf86WriteSerial(fd, &b, 1) != 1) {
120
	PS2DBG(ErrorF("ps2_putbyte: error xf86WriteSerial\n"));
121
	return FALSE;
122
    }
123
    PS2DBG(ErrorF("ps2_putbyte: byte %02X send\n", b));
124
    /* wait for an ACK */
125 126
    if (!ps2_getbyte(fd, &ack)) {
	return FALSE;
127
    }
128
    if (ack != PS2_ACK) {
129
	PS2DBG(ErrorF("ps2_putbyte: wrong acknowledge 0x%02x\n", ack));
130
	return FALSE;
131
    }
132
    return TRUE;
133 134 135
}

/*
136 137
 * Use the Synaptics extended ps/2 syntax to write a special command byte. Needed by
 * ps2_send_cmd and ps2_set_mode.
138 139 140
 * 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)
 */
141 142
static Bool
ps2_special_cmd(int fd, byte cmd)
143
{
144 145 146
    int i;

    /* initialize with 'inert' command */
147 148 149 150 151 152 153 154 155 156 157
    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;
158 159 160 161
}

/*
 * Send a command to the synpatics touchpad by special commands
162
 */
163 164 165
static Bool
ps2_send_cmd(int fd, byte c)
{
166
    PS2DBG(ErrorF("send command: 0x%02X\n", c));
167 168
    return (ps2_special_cmd(fd, c) &&
	    ps2_putbyte(fd, PS2_CMD_STATUS_REQUEST));
169 170
}

171 172 173 174 175 176 177 178 179 180 181 182 183 184
/*****************************************************************************
 *	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;
185
	 !ps2_getbyte(fd, &ack) && (timeout_count <= MAX_RETRY_COUNT);
186 187 188 189
	 timeout_count++)
	;
    /* Do some sanity checking */
    if ((ack & 0xfc) != 0x84) {
190 191
	PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0x84 and got: %02x\n",
		      ack & 0xfc));
192
	return FALSE;
193 194 195 196 197 198
    }

    ps2_getbyte(fd, response);
    ps2_getbyte(fd, &ack);
    ps2_getbyte(fd, &ack);
    if ((ack & 0xcc) != 0xc4) {
199 200
	PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0xc4 and got: %02x\n",
		      ack & 0xcc));
201
	return FALSE;
202 203 204 205
    }
    ps2_getbyte(fd, &ack);
    ps2_getbyte(fd, &ack);

206
    return TRUE;
207 208 209 210 211 212 213 214 215 216 217 218 219
}

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) {
220
	PS2DBG(ErrorF("ps2_putbyte_passthrough: wrong acknowledge 0x%02x\n", ack));
221
	return FALSE;
222
    }
223
    return TRUE;
224 225
}

226 227 228 229 230 231 232
/*****************************************************************************
 *	Synaptics communications functions
 ****************************************************************************/

/*
 * Set the synaptics touchpad mode byte by special commands
 */
233
static Bool
234
ps2_synaptics_set_mode(int fd, byte mode)
235
{
236
    PS2DBG(ErrorF("set mode byte to: 0x%02X\n", mode));
237 238
    return (ps2_special_cmd(fd, mode) &&
	    ps2_putbyte(fd, PS2_CMD_SET_SAMPLE_RATE) &&
239
	    ps2_putbyte(fd, 0x14));
240
}
241 242 243

/*
 * reset the touchpad
244
 */
245
static Bool
246
ps2_synaptics_reset(int fd)
247
{
248
    byte r[2];
249

250
    xf86FlushInput(fd);
251
    PS2DBG(ErrorF("Reset the Touchpad...\n"));
252
    if (!ps2_putbyte(fd, PS2_CMD_RESET)) {
253
	PS2DBG(ErrorF("...failed\n"));
254
	return FALSE;
255 256
    }
    xf86WaitForInput(fd, 4000000);
257
    if (ps2_getbyte(fd, &r[0]) && ps2_getbyte(fd, &r[1])) {
258
	if (r[0] == 0xAA && r[1] == 0x00) {
259
	    PS2DBG(ErrorF("...done\n"));
260
	    return TRUE;
261
	} else {
262
	    PS2DBG(ErrorF("...failed. Wrong reset ack 0x%02x, 0x%02x\n", r[0], r[1]));
263
	    return FALSE;
264
	}
265
    }
266
    PS2DBG(ErrorF("...failed\n"));
267
    return FALSE;
268 269
}

270
static Bool
271
ps2_synaptics_reset_passthrough(int fd)
272 273 274 275 276 277 278
{
    byte ack;

    /* send reset */
    ps2_putbyte_passthrough(fd, 0xff);
    ps2_getbyte_passthrough(fd, &ack);
    if (ack != 0xaa) {
279
	PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0xaa\n", ack));
280
	return FALSE;
281 282 283
    }
    ps2_getbyte_passthrough(fd, &ack);
    if (ack != 0x00) {
284
	PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0x00\n", ack));
285
	return FALSE;
286 287 288
    }

    /* set defaults, turn on streaming, and enable the mouse */
289 290
    return (ps2_putbyte_passthrough(fd, 0xf6) &&
	    ps2_putbyte_passthrough(fd, 0xea) &&
291 292 293
	    ps2_putbyte_passthrough(fd, 0xf4));
}

294
/*
295 296 297
 * Read the model-id bytes from the touchpad
 * see also SYN_MODEL_* macros
 */
298
static Bool
299
ps2_synaptics_model_id(int fd, struct SynapticsHwInfo *synhw)
300
{
301
    byte mi[3];
302

303
    PS2DBG(ErrorF("Read mode id...\n"));
304

305 306 307 308 309
    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])) {
310
	synhw->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
311
	PS2DBG(ErrorF("model-id %06X\n", synhw->model_id));
312
	PS2DBG(ErrorF("...done.\n"));
313
	return TRUE;
314
    }
315
    PS2DBG(ErrorF("...failed.\n"));
316
    return FALSE;
317 318 319 320 321 322
}

/*
 * Read the capability-bits from the touchpad
 * see also the SYN_CAP_* macros
 */
323
static Bool
324
ps2_synaptics_capability(int fd, struct SynapticsHwInfo *synhw)
325
{
326
    byte cap[3];
327

328
    PS2DBG(ErrorF("Read capabilites...\n"));
329

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

/*
360
 * Identify Touchpad
361 362
 * See also the SYN_ID_* macros
 */
363
static Bool
364
ps2_synaptics_identify(int fd, struct SynapticsHwInfo *synhw)
365
{
366
    byte id[3];
367

368
    PS2DBG(ErrorF("Identify Touchpad...\n"));
369

370
    synhw->identity = 0;
371 372 373 374
    if (ps2_send_cmd(fd, SYN_QUE_IDENTIFY) &&
	ps2_getbyte(fd, &id[0]) &&
	ps2_getbyte(fd, &id[1]) &&
	ps2_getbyte(fd, &id[2])) {
375
	synhw->identity = (id[0] << 16) | (id[1] << 8) | id[2];
376
	PS2DBG(ErrorF("ident %06X\n", synhw->identity));
377
	if (SYN_ID_IS_SYNAPTICS(synhw)) {
378
	    PS2DBG(ErrorF("...done.\n"));
379
	    return TRUE;
380
	}
381
    }
382
    PS2DBG(ErrorF("...failed.\n"));
383
    return FALSE;
384 385
}

386
static Bool
387
ps2_synaptics_enable_device(int fd)
388
{
389
    return ps2_putbyte(fd, PS2_CMD_ENABLE);
390 391
}

392
static Bool
393
ps2_synaptics_disable_device(int fd)
394
{
395 396
    xf86FlushInput(fd);
    return ps2_putbyte(fd, PS2_CMD_DISABLE);
397 398
}

399
static Bool
400
ps2_query_is_synaptics(int fd, struct SynapticsHwInfo* synhw)
401
{
402 403 404
    int i;

    for (i = 0; i < 3; i++) {
405
	if (ps2_synaptics_disable_device(fd))
406 407 408 409 410
	    break;
    }

    xf86WaitForInput(fd, 20000);
    xf86FlushInput(fd);
411
    if (ps2_synaptics_identify(fd, synhw)) {
412 413
	return TRUE;
    } else {
414
	ErrorF("Query no Synaptics: %06X\n", synhw->identity);
415 416
	return FALSE;
    }
417
}
418

419
void
420
ps2_print_ident(const struct SynapticsHwInfo *synhw)
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));
425

426
    if (SYN_MODEL_ROT180(synhw))
427
	xf86Msg(X_PROBED, " 180 degree mounted touchpad\n");
428
    if (SYN_MODEL_PORTRAIT(synhw))
429
	xf86Msg(X_PROBED, " portrait touchpad\n");
430 431
    xf86Msg(X_PROBED, " Sensor: %d\n", SYN_MODEL_SENSOR(synhw));
    if (SYN_MODEL_NEWABS(synhw))
432
	xf86Msg(X_PROBED, " new absolute packet format\n");
433
    if (SYN_MODEL_PEN(synhw))
434 435
	xf86Msg(X_PROBED, " pen detection\n");

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

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

461
static Bool
462
PS2QueryHardware(LocalDevicePtr local)
463 464
{
    int mode;
465 466 467 468 469 470
    SynapticsPrivate *priv = (SynapticsPrivate *)local->private;
    struct SynapticsHwInfo *synhw;

    if (!priv->proto_data)
        priv->proto_data = xcalloc(1, sizeof(struct SynapticsHwInfo));
    synhw = (struct SynapticsHwInfo*)priv->proto_data;
471 472

    /* is the synaptics touchpad active? */
473
    if (!ps2_query_is_synaptics(local->fd, synhw))
474 475 476 477
	return FALSE;

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

478
    if (!ps2_synaptics_reset(local->fd))
479 480
	xf86Msg(X_ERROR, "%s reset failed\n", local->name);

481
    if (!ps2_synaptics_identify(local->fd, synhw))
482 483
	return FALSE;

484
    if (!ps2_synaptics_model_id(local->fd, synhw))
485 486
	return FALSE;

487
    if (!ps2_synaptics_capability(local->fd, synhw))
488 489 490
	return FALSE;

    mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
491
    if (SYN_ID_MAJOR(synhw) >= 4)
492
	mode |= SYN_BIT_DISABLE_GESTURE;
493
    if (SYN_CAP_EXTENDED(synhw))
494
	mode |= SYN_BIT_W_MODE;
495
    if (!ps2_synaptics_set_mode(local->fd, mode))
496 497 498
	return FALSE;

    /* Check to see if the host mouse supports a guest */
499
    synhw->hasGuest = FALSE;
500
    if (SYN_CAP_PASSTHROUGH(synhw)) {
501
        synhw->hasGuest = TRUE;
502 503 504 505 506

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

	/* Disable the host to talk to the guest */
507
	ps2_synaptics_disable_device(local->fd);
508
	/* Reset it, set defaults, streaming and enable it */
509
	if (!ps2_synaptics_reset_passthrough(local->fd)) {
510
	    synhw->hasGuest = FALSE;
511 512 513
	}
    }

514
    ps2_synaptics_enable_device(local->fd);
515

516
    ps2_print_ident(synhw);
517 518 519 520

    return TRUE;
}

521 522 523 524
/*
 * Decide if the current packet stored in priv->protoBuf is valid.
 */
static Bool
525
ps2_packet_ok(struct SynapticsHwInfo *synhw, struct CommData *comm)
526
{
527
    unsigned char *buf = comm->protoBuf;
528
    int newabs = SYN_MODEL_NEWABS(synhw);
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

    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
554
ps2_synaptics_get_packet(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
555
			 struct SynapticsProtocolOperations *proto_ops,
556
			 struct CommData *comm)
557 558 559 560 561
{
    int count = 0;
    int c;
    unsigned char u;

562
    while ((c = XisbRead(comm->buffer)) >= 0) {
563 564 565
	u = (unsigned char)c;

	/* test if there is a reset sequence received */
566
	if ((c == 0x00) && (comm->lastByte == 0xAA)) {
567 568
	    if (xf86WaitForInput(local->fd, 50000) == 0) {
		DBG(7, ErrorF("Reset received\n"));
569
		proto_ops->QueryHardware(local);
570 571 572
	    } else
		DBG(3, ErrorF("faked reset received\n"));
	}
573
	comm->lastByte = u;
574 575 576 577 578 579 580

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

581
	comm->protoBuf[comm->protoBufTail++] = u;
582 583 584

	/* Check that we have a valid packet. If not, we are out of sync,
	   so we throw away the first byte in the packet.*/
585
	if (comm->protoBufTail >= 6) {
586
	    if (!ps2_packet_ok(synhw, comm)) {
587
		int i;
588 589 590 591 592 593
		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;
594
		    DBG(3, ErrorF("Synaptics synchronization lost too long -> reset touchpad.\n"));
595
		    proto_ops->QueryHardware(local); /* including a reset */
596 597 598 599 600
		    continue;
		}
	    }
	}

601 602 603
	if (comm->protoBufTail >= 6) { /* Full packet received */
	    if (comm->outOfSync > 0) {
		comm->outOfSync = 0;
604 605
		DBG(4, ErrorF("Synaptics driver resynced.\n"));
	    }
606
	    comm->protoBufTail = 0;
607 608 609 610 611 612 613 614
	    return TRUE;
	}
    }

    return FALSE;
}

static Bool
615
PS2ReadHwState(LocalDevicePtr local,
616
	       struct SynapticsProtocolOperations *proto_ops,
617
	       struct CommData *comm, struct SynapticsHwState *hwRet)
618
{
619 620
    unsigned char *buf = comm->protoBuf;
    struct SynapticsHwState *hw = &(comm->hwState);
621 622
    SynapticsPrivate *priv = (SynapticsPrivate *)local->private;
    SynapticsParameters *para = &priv->synpara;
623 624
    struct SynapticsHwInfo *synhw;
    int newabs;
625 626
    int w, i;

627 628 629 630 631 632 633 634 635 636 637
    synhw = (struct SynapticsHwInfo*)priv->proto_data;
    if (!synhw)
    {
        xf86Msg(X_ERROR,
                "%s: PS2ReadHwState, synhw is NULL. This is a bug.\n",
                local->name);
        return FALSE;
    }

    newabs = SYN_MODEL_NEWABS(synhw);

638
    if (!ps2_synaptics_get_packet(local, synhw, proto_ops, comm))
639 640 641 642
	return FALSE;

    /* Handle guest packets */
    hw->guest_dx = hw->guest_dy = 0;
643
    if (newabs && synhw->hasGuest) {
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
	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;

683 684
	if (SYN_CAP_EXTENDED(synhw)) {
	    if (SYN_CAP_MIDDLE_BUTTON(synhw)) {
685 686
		hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
	    }
687
	    if (SYN_CAP_FOUR_BUTTON(synhw)) {
688 689 690 691 692 693 694
		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;
	    }
695
	    if (SYN_CAP_MULTI_BUTTON_NO(synhw)) {
696
		if ((buf[3] & 2) ? !hw->right : hw->right) {
697
		    switch (SYN_CAP_MULTI_BUTTON_NO(synhw) & ~0x01) {
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
		    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;

734
    if (hw->z >= para->finger_high) {
735 736 737 738 739 740
	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.
	 */
741
	if (SYN_CAP_EXTENDED(synhw)) {
742
	    if ((w >= 0) && (w <= 1)) {
743
		w_ok = SYN_CAP_MULTIFINGER(synhw);
744
	    } else if (w == 2) {
745
		w_ok = SYN_MODEL_PEN(synhw);
746
	    } else if ((w >= 4) && (w <= 15)) {
747
		w_ok = SYN_CAP_PALMDETECT(synhw);
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
	    }
	}
	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;
}

773 774
static Bool
PS2AutoDevProbe(LocalDevicePtr local)
775 776 777 778
{
    return FALSE;
}

779
struct SynapticsProtocolOperations psaux_proto_operations = {
780
    NULL,
781
    PS2DeviceOffHook,
782
    PS2QueryHardware,
783
    PS2ReadHwState,
784
    PS2AutoDevProbe,
785
    NULL /* ReadDevDimensions */
786
};