ps2comm.c 17.8 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 "synproto.h"
41
#include "synaptics.h"
42
#include "synapticsstr.h"
43
#include "ps2comm.h"
44
#include <xf86.h>
45

46
#define MAX_UNSYNC_PACKETS 10				/* i.e. 10 to 60 bytes */
47 48 49 50 51 52 53 54 55 56 57
/*
 * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
 * section 2.3.2, which says that they should be valid regardless of the
 * actual size of the sensor.
 */
#define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448

#define XMAX_VALID 6143
58

59
/* synaptics queries */
60 61 62 63 64 65 66 67
#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
68 69

/* status request response bits (PS2_CMD_STATUS_REQUEST) */
70 71 72 73 74 75 76 77
#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)
78

79
#ifdef DEBUG
80
#define PS2DBG(x) (x)
81
#else
82
#define PS2DBG(x)
83
#endif
84 85 86 87 88 89

/*****************************************************************************
 *	PS/2 Utility functions.
 *     Many parts adapted from tpconfig.c by C. Scott Ananian
 ****************************************************************************/

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

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

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

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

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

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

/*****************************************************************************
 *	Synaptics communications functions
 ****************************************************************************/

/*
 * Set the synaptics touchpad mode byte by special commands
 */
175
static Bool
176
ps2_synaptics_set_mode(int fd, byte mode)
177
{
178
    PS2DBG(ErrorF("set mode byte to: 0x%02X\n", mode));
179 180
    return (ps2_special_cmd(fd, mode) &&
	    ps2_putbyte(fd, PS2_CMD_SET_SAMPLE_RATE) &&
181
	    ps2_putbyte(fd, 0x14));
182
}
183 184 185

/*
 * reset the touchpad
186
 */
187
static Bool
188
ps2_synaptics_reset(int fd)
189
{
190
    byte r[2];
191

192
    xf86FlushInput(fd);
193
    PS2DBG(ErrorF("Reset the Touchpad...\n"));
194
    if (!ps2_putbyte(fd, PS2_CMD_RESET)) {
195
	PS2DBG(ErrorF("...failed\n"));
196
	return FALSE;
197 198
    }
    xf86WaitForInput(fd, 4000000);
199
    if (ps2_getbyte(fd, &r[0]) && ps2_getbyte(fd, &r[1])) {
200
	if (r[0] == 0xAA && r[1] == 0x00) {
201
	    PS2DBG(ErrorF("...done\n"));
202
	    return TRUE;
203
	} else {
204
	    PS2DBG(ErrorF("...failed. Wrong reset ack 0x%02x, 0x%02x\n", r[0], r[1]));
205
	    return FALSE;
206
	}
207
    }
208
    PS2DBG(ErrorF("...failed\n"));
209
    return FALSE;
210 211
}

212
/*
213 214 215
 * Read the model-id bytes from the touchpad
 * see also SYN_MODEL_* macros
 */
216
static Bool
217
ps2_synaptics_model_id(int fd, struct PS2SynapticsHwInfo *synhw)
218
{
219
    byte mi[3];
220

221
    PS2DBG(ErrorF("Read mode id...\n"));
222

223 224 225 226 227
    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])) {
228
	synhw->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
229
	PS2DBG(ErrorF("model-id %06X\n", synhw->model_id));
230
	PS2DBG(ErrorF("...done.\n"));
231
	return TRUE;
232
    }
233
    PS2DBG(ErrorF("...failed.\n"));
234
    return FALSE;
235 236 237 238 239 240
}

/*
 * Read the capability-bits from the touchpad
 * see also the SYN_CAP_* macros
 */
241
static Bool
242
ps2_synaptics_capability(int fd, struct PS2SynapticsHwInfo *synhw)
243
{
244
    byte cap[3];
245

246
    PS2DBG(ErrorF("Read capabilites...\n"));
247

248
    synhw->capabilities = 0;
249
    synhw->ext_cap = 0;
250 251 252 253
    if (ps2_send_cmd(fd, SYN_QUE_CAPABILITIES) &&
	ps2_getbyte(fd, &cap[0]) &&
	ps2_getbyte(fd, &cap[1]) &&
	ps2_getbyte(fd, &cap[2])) {
254
	synhw->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
255
	PS2DBG(ErrorF("capabilities %06X\n", synhw->capabilities));
256 257
	if (SYN_CAP_VALID(synhw)) {
	    if (SYN_EXT_CAP_REQUESTS(synhw)) {
258 259 260 261
		if (ps2_send_cmd(fd, SYN_QUE_EXT_CAPAB) &&
		    ps2_getbyte(fd, &cap[0]) &&
		    ps2_getbyte(fd, &cap[1]) &&
		    ps2_getbyte(fd, &cap[2])) {
262
		    synhw->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
263
		    PS2DBG(ErrorF("ext-capability %06X\n", synhw->ext_cap));
264
		} else {
265 266
		    PS2DBG(ErrorF("synaptics says, that it has extended-capabilities, "
				  "but I cannot read them."));
267 268
		}
	    }
269
	    PS2DBG(ErrorF("...done.\n"));
270
	    return TRUE;
271
	}
272
    }
273
    PS2DBG(ErrorF("...failed.\n"));
274
    return FALSE;
275 276 277
}

/*
278
 * Identify Touchpad
279 280
 * See also the SYN_ID_* macros
 */
281
static Bool
282
ps2_synaptics_identify(int fd, struct PS2SynapticsHwInfo *synhw)
283
{
284
    byte id[3];
285

286
    PS2DBG(ErrorF("Identify Touchpad...\n"));
287

288
    synhw->identity = 0;
289 290 291 292
    if (ps2_send_cmd(fd, SYN_QUE_IDENTIFY) &&
	ps2_getbyte(fd, &id[0]) &&
	ps2_getbyte(fd, &id[1]) &&
	ps2_getbyte(fd, &id[2])) {
293
	synhw->identity = (id[0] << 16) | (id[1] << 8) | id[2];
294
	PS2DBG(ErrorF("ident %06X\n", synhw->identity));
295
	if (SYN_ID_IS_SYNAPTICS(synhw)) {
296
	    PS2DBG(ErrorF("...done.\n"));
297
	    return TRUE;
298
	}
299
    }
300
    PS2DBG(ErrorF("...failed.\n"));
301
    return FALSE;
302 303
}

304
static Bool
305
ps2_synaptics_enable_device(int fd)
306
{
307
    return ps2_putbyte(fd, PS2_CMD_ENABLE);
308 309
}

310
static Bool
311
ps2_synaptics_disable_device(int fd)
312
{
313 314
    xf86FlushInput(fd);
    return ps2_putbyte(fd, PS2_CMD_DISABLE);
315 316
}

317
static Bool
318
ps2_query_is_synaptics(InputInfoPtr pInfo, int fd, struct PS2SynapticsHwInfo* synhw)
319
{
320 321 322
    int i;

    for (i = 0; i < 3; i++) {
323
	if (ps2_synaptics_disable_device(fd))
324 325 326 327 328
	    break;
    }

    xf86WaitForInput(fd, 20000);
    xf86FlushInput(fd);
329
    if (ps2_synaptics_identify(fd, synhw)) {
330 331
	return TRUE;
    } else {
332
	xf86IDrvMsg(pInfo, X_ERROR, "Query no Synaptics: %06X\n", synhw->identity);
333 334
	return FALSE;
    }
335
}
336

337
void
338
ps2_print_ident(InputInfoPtr pInfo, const struct PS2SynapticsHwInfo *synhw)
339
{
340 341
    xf86IDrvMsg(pInfo, X_PROBED, " Synaptics Touchpad, model: %d\n", SYN_ID_MODEL(synhw));
    xf86IDrvMsg(pInfo, X_PROBED, " Firmware: %d.%d\n", SYN_ID_MAJOR(synhw),
342
	    SYN_ID_MINOR(synhw));
343

344
    if (SYN_MODEL_ROT180(synhw))
345
	xf86IDrvMsg(pInfo, X_PROBED, " 180 degree mounted touchpad\n");
346
    if (SYN_MODEL_PORTRAIT(synhw))
347 348
	xf86IDrvMsg(pInfo, X_PROBED, " portrait touchpad\n");
    xf86IDrvMsg(pInfo, X_PROBED, " Sensor: %d\n", SYN_MODEL_SENSOR(synhw));
349
    if (SYN_MODEL_NEWABS(synhw))
350
	xf86IDrvMsg(pInfo, X_PROBED, " new absolute packet format\n");
351
    if (SYN_MODEL_PEN(synhw))
352
	xf86IDrvMsg(pInfo, X_PROBED, " pen detection\n");
353

354
    if (SYN_CAP_EXTENDED(synhw)) {
355
	xf86IDrvMsg(pInfo, X_PROBED, " Touchpad has extended capability bits\n");
356
	if (SYN_CAP_MULTI_BUTTON_NO(synhw))
357
	    xf86IDrvMsg(pInfo, X_PROBED, " -> %d multi buttons, i.e. besides standard buttons\n",
358 359
		    (int)(SYN_CAP_MULTI_BUTTON_NO(synhw)));
	if (SYN_CAP_MIDDLE_BUTTON(synhw))
360
	    xf86IDrvMsg(pInfo, X_PROBED, " -> middle button\n");
361
	if (SYN_CAP_FOUR_BUTTON(synhw))
362
	    xf86IDrvMsg(pInfo, X_PROBED, " -> four buttons\n");
363
	if (SYN_CAP_MULTIFINGER(synhw))
364
	    xf86IDrvMsg(pInfo, X_PROBED, " -> multifinger detection\n");
365
	if (SYN_CAP_PALMDETECT(synhw))
366
	    xf86IDrvMsg(pInfo, X_PROBED, " -> palm detection\n");
367
	if (SYN_CAP_PASSTHROUGH(synhw))
368
	    xf86IDrvMsg(pInfo, X_PROBED, " -> pass-through port\n");
369 370 371
    }
}

372
static void
373
PS2DeviceOffHook(InputInfoPtr pInfo)
374
{
375 376
    ps2_synaptics_reset(pInfo->fd);
    ps2_synaptics_enable_device(pInfo->fd);
377 378
}

379
static Bool
380
PS2QueryHardware(InputInfoPtr pInfo)
381 382
{
    int mode;
383
    SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private;
384
    struct PS2SynapticsHwInfo *synhw;
385 386

    if (!priv->proto_data)
387 388
        priv->proto_data = calloc(1, sizeof(struct PS2SynapticsHwInfo));
    synhw = (struct PS2SynapticsHwInfo*)priv->proto_data;
389 390

    /* is the synaptics touchpad active? */
391
    if (!ps2_query_is_synaptics(pInfo, pInfo->fd, synhw))
392 393
	return FALSE;

394
    xf86IDrvMsg(pInfo, X_PROBED, "synaptics touchpad found\n");
395

396
    if (!ps2_synaptics_reset(pInfo->fd))
397
	xf86IDrvMsg(pInfo, X_ERROR, "reset failed\n");
398

399
    if (!ps2_synaptics_identify(pInfo->fd, synhw))
400 401
	return FALSE;

402
    if (!ps2_synaptics_model_id(pInfo->fd, synhw))
403 404
	return FALSE;

405
    if (!ps2_synaptics_capability(pInfo->fd, synhw))
406 407 408
	return FALSE;

    mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
409
    if (SYN_ID_MAJOR(synhw) >= 4)
410
	mode |= SYN_BIT_DISABLE_GESTURE;
411
    if (SYN_CAP_EXTENDED(synhw))
412
	mode |= SYN_BIT_W_MODE;
413
    if (!ps2_synaptics_set_mode(pInfo->fd, mode))
414 415
	return FALSE;

416
    ps2_synaptics_enable_device(pInfo->fd);
417

418
    ps2_print_ident(pInfo, synhw);
419 420 421 422

    return TRUE;
}

423 424 425 426
/*
 * Decide if the current packet stored in priv->protoBuf is valid.
 */
static Bool
427
ps2_packet_ok(struct PS2SynapticsHwInfo *synhw, struct CommData *comm)
428
{
429
    unsigned char *buf = comm->protoBuf;
430
    int newabs = SYN_MODEL_NEWABS(synhw);
431 432

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

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

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

    if (!newabs && ((buf[4] & 0x60) != 0x00)) {
448
	DBG(4, "Synaptics driver lost sync at 5th byte\n");
449 450 451 452 453 454 455
	return FALSE;
    }

    return TRUE;
}

static Bool
456
ps2_synaptics_get_packet(InputInfoPtr pInfo, struct PS2SynapticsHwInfo *synhw,
457
			 struct SynapticsProtocolOperations *proto_ops,
458
			 struct CommData *comm)
459 460 461 462 463
{
    int count = 0;
    int c;
    unsigned char u;

464
    while ((c = XisbRead(comm->buffer)) >= 0) {
465 466 467
	u = (unsigned char)c;

	/* test if there is a reset sequence received */
468
	if ((c == 0x00) && (comm->lastByte == 0xAA)) {
469
	    if (xf86WaitForInput(pInfo->fd, 50000) == 0) {
470
		DBG(7, "Reset received\n");
471
		proto_ops->QueryHardware(pInfo);
472
	    } else
473
		DBG(3, "faked reset received\n");
474
	}
475
	comm->lastByte = u;
476 477 478

	/* to avoid endless loops */
	if (count++ > 30) {
479
	    xf86IDrvMsg(pInfo, X_ERROR, "Synaptics driver lost sync... got gigantic packet!\n");
480 481 482
	    return FALSE;
	}

483
	comm->protoBuf[comm->protoBufTail++] = u;
484 485 486

	/* Check that we have a valid packet. If not, we are out of sync,
	   so we throw away the first byte in the packet.*/
487
	if (comm->protoBufTail >= 6) {
488
	    if (!ps2_packet_ok(synhw, comm)) {
489
		int i;
490 491 492 493 494 495
		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;
496
		    DBG(3, "Synaptics synchronization lost too long -> reset touchpad.\n");
497
		    proto_ops->QueryHardware(pInfo); /* including a reset */
498 499 500 501 502
		    continue;
		}
	    }
	}

503 504 505
	if (comm->protoBufTail >= 6) { /* Full packet received */
	    if (comm->outOfSync > 0) {
		comm->outOfSync = 0;
506
		DBG(4, "Synaptics driver resynced.\n");
507
	    }
508
	    comm->protoBufTail = 0;
509 510 511 512 513 514 515
	    return TRUE;
	}
    }

    return FALSE;
}

516 517
Bool
PS2ReadHwStateProto(InputInfoPtr pInfo,
518
	       struct SynapticsProtocolOperations *proto_ops,
519
	       struct CommData *comm, struct SynapticsHwState *hwRet)
520
{
521 522
    unsigned char *buf = comm->protoBuf;
    struct SynapticsHwState *hw = &(comm->hwState);
523
    SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private;
524
    SynapticsParameters *para = &priv->synpara;
525
    struct PS2SynapticsHwInfo *synhw;
526
    int newabs;
527
    int w, i;
528

529
    synhw = (struct PS2SynapticsHwInfo*)priv->proto_data;
530 531
    if (!synhw)
    {
532 533
        xf86IDrvMsg(pInfo, X_ERROR,
                    "PS2ReadHwState, synhw is NULL. This is a bug.\n");
534 535 536 537 538
        return FALSE;
    }

    newabs = SYN_MODEL_NEWABS(synhw);

539
    if (!ps2_synaptics_get_packet(pInfo, synhw, proto_ops, comm))
540 541 542
	return FALSE;

    /* Handle normal packets */
543
    hw->x = hw->y = hw->z = hw->numFingers = hw->fingerWidth = 0;
544 545 546 547 548
    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...*/
549
	DBG(7, "using new protocols\n");
550
	hw->x = (((buf[3] & 0x10) << 8) |
551 552
		 ((buf[1] & 0x0f) << 8) |
		 buf[4]);
553
	hw->y = (((buf[3] & 0x20) << 7) |
554 555 556 557 558 559 560 561 562 563 564
		 ((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;

565 566
	if (SYN_CAP_EXTENDED(synhw)) {
	    if (SYN_CAP_MIDDLE_BUTTON(synhw)) {
567 568
		hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
	    }
569
	    if (SYN_CAP_FOUR_BUTTON(synhw)) {
570 571 572 573 574 575 576
		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;
	    }
577
	    if (SYN_CAP_MULTI_BUTTON_NO(synhw)) {
578
		if ((buf[3] & 2) ? !hw->right : hw->right) {
579
		    switch (SYN_CAP_MULTI_BUTTON_NO(synhw) & ~0x01) {
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
		    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...*/
599
	DBG(7, "using old protocol\n");
600
	hw->x = (((buf[1] & 0x1F) << 8) |
601
		 buf[2]);
602
	hw->y = (((buf[4] & 0x1F) << 8) |
603 604 605 606 607 608 609 610 611 612 613
		 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;
    }

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

616
    if (hw->z >= para->finger_high) {
617 618 619 620 621 622
	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.
	 */
623
	if (SYN_CAP_EXTENDED(synhw)) {
624
	    if ((w >= 0) && (w <= 1)) {
625
		w_ok = SYN_CAP_MULTIFINGER(synhw);
626
	    } else if (w == 2) {
627
		w_ok = SYN_MODEL_PEN(synhw);
628
	    } else if ((w >= 4) && (w <= 15)) {
629
		w_ok = SYN_CAP_PALMDETECT(synhw);
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
	    }
	}
	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;
}

655 656 657 658 659 660 661
static Bool
PS2ReadHwState(InputInfoPtr pInfo,
               struct CommData *comm, struct SynapticsHwState *hwRet)
{
    return PS2ReadHwStateProto(pInfo, &psaux_proto_operations, comm, hwRet);
}

662
struct SynapticsProtocolOperations psaux_proto_operations = {
663
    NULL,
664
    PS2DeviceOffHook,
665
    PS2QueryHardware,
666
    PS2ReadHwState,
667
    NULL,
668
    NULL
669
};