summaryrefslogtreecommitdiff
path: root/src/ps2comm.c
diff options
context:
space:
mode:
authorChristoph Brill <egore911@egore911.de>2008-06-06 15:58:17 +0200
committerChristoph Brill <egore911@egore911.de>2008-06-06 15:58:17 +0200
commit86a5fab70ff633d40c05a37d8e9a6a8073cdb129 (patch)
treebbda0908c55897c5f82d28a37d75eb8d1b4ed8c1 /src/ps2comm.c
parentcd6a1225ec319cad9788e8fba158d9792b55de23 (diff)
Start reorganizing the source tree
First let's move the source and header files to a seperate source directory. The structure of the new directory layout will be similar to xf86-input-evdev.
Diffstat (limited to 'src/ps2comm.c')
-rw-r--r--src/ps2comm.c751
1 files changed, 751 insertions, 0 deletions
diff --git a/src/ps2comm.c b/src/ps2comm.c
new file mode 100644
index 0000000..fe1b929
--- /dev/null
+++ b/src/ps2comm.c
@@ -0,0 +1,751 @@
+/* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
+ *
+ * Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
+ * Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
+ * code für the special synaptics commands (from the tpconfig-source)
+ *
+ * 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>
+ *
+ * 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.
+ *
+ */
+
+#include "ps2comm.h"
+#include "synproto.h"
+#include "synaptics.h"
+#include <xf86.h>
+
+#define MAX_UNSYNC_PACKETS 10 /* i.e. 10 to 60 bytes */
+
+/* synaptics queries */
+#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
+
+/* status request response bits (PS2_CMD_STATUS_REQUEST) */
+#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)
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+#define PS2DBG(x) (x)
+#else
+#define PS2DBG(x)
+#endif
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 1
+#define DBG(a,b)
+#endif
+
+/*****************************************************************************
+ * PS/2 Utility functions.
+ * Many parts adapted from tpconfig.c by C. Scott Ananian
+ ****************************************************************************/
+
+/*
+ * Read a byte from the ps/2 port
+ */
+static Bool
+ps2_getbyte(int fd, byte *b)
+{
+ if (xf86WaitForInput(fd, 50000) > 0) {
+ if (xf86ReadSerial(fd, b, 1) != 1) {
+ PS2DBG(ErrorF("ps2_getbyte: No byte read\n"));
+ return FALSE;
+ }
+ PS2DBG(ErrorF("ps2_getbyte: byte %02X read\n", *b));
+ return TRUE;
+ }
+ PS2DBG(ErrorF("ps2_getbyte: timeout xf86WaitForInput\n"));
+ return FALSE;
+}
+
+/*
+ * Write a byte to the ps/2 port, wait for ACK
+ */
+Bool
+ps2_putbyte(int fd, byte b)
+{
+ byte ack;
+
+ if (xf86WriteSerial(fd, &b, 1) != 1) {
+ PS2DBG(ErrorF("ps2_putbyte: error xf86WriteSerial\n"));
+ return FALSE;
+ }
+ PS2DBG(ErrorF("ps2_putbyte: byte %02X send\n", b));
+ /* wait for an ACK */
+ if (!ps2_getbyte(fd, &ack)) {
+ return FALSE;
+ }
+ if (ack != PS2_ACK) {
+ PS2DBG(ErrorF("ps2_putbyte: wrong acknowledge 0x%02x\n", ack));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Use the Synaptics extended ps/2 syntax to write a special command byte. Needed by
+ * ps2_send_cmd and ps2_set_mode.
+ * 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)
+ */
+static Bool
+ps2_special_cmd(int fd, byte cmd)
+{
+ int i;
+
+ /* initialize with 'inert' command */
+ 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;
+}
+
+/*
+ * Send a command to the synpatics touchpad by special commands
+ */
+static Bool
+ps2_send_cmd(int fd, byte c)
+{
+ PS2DBG(ErrorF("send command: 0x%02X\n", c));
+ return (ps2_special_cmd(fd, c) &&
+ ps2_putbyte(fd, PS2_CMD_STATUS_REQUEST));
+}
+
+/*****************************************************************************
+ * 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;
+ !ps2_getbyte(fd, &ack) && (timeout_count <= MAX_RETRY_COUNT);
+ timeout_count++)
+ ;
+ /* Do some sanity checking */
+ if ((ack & 0xfc) != 0x84) {
+ PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0x84 and got: %02x\n",
+ ack & 0xfc));
+ return FALSE;
+ }
+
+ ps2_getbyte(fd, response);
+ ps2_getbyte(fd, &ack);
+ ps2_getbyte(fd, &ack);
+ if ((ack & 0xcc) != 0xc4) {
+ PS2DBG(ErrorF("ps2_getbyte_passthrough: expected 0xc4 and got: %02x\n",
+ ack & 0xcc));
+ return FALSE;
+ }
+ ps2_getbyte(fd, &ack);
+ ps2_getbyte(fd, &ack);
+
+ return TRUE;
+}
+
+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) {
+ PS2DBG(ErrorF("ps2_putbyte_passthrough: wrong acknowledge 0x%02x\n", ack));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*****************************************************************************
+ * Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Set the synaptics touchpad mode byte by special commands
+ */
+static Bool
+ps2_synaptics_set_mode(int fd, byte mode)
+{
+ PS2DBG(ErrorF("set mode byte to: 0x%02X\n", mode));
+ return (ps2_special_cmd(fd, mode) &&
+ ps2_putbyte(fd, PS2_CMD_SET_SAMPLE_RATE) &&
+ ps2_putbyte(fd, 0x14));
+}
+
+/*
+ * reset the touchpad
+ */
+static Bool
+ps2_synaptics_reset(int fd)
+{
+ byte r[2];
+
+ xf86FlushInput(fd);
+ PS2DBG(ErrorF("Reset the Touchpad...\n"));
+ if (!ps2_putbyte(fd, PS2_CMD_RESET)) {
+ PS2DBG(ErrorF("...failed\n"));
+ return FALSE;
+ }
+ xf86WaitForInput(fd, 4000000);
+ if (ps2_getbyte(fd, &r[0]) && ps2_getbyte(fd, &r[1])) {
+ if (r[0] == 0xAA && r[1] == 0x00) {
+ PS2DBG(ErrorF("...done\n"));
+ return TRUE;
+ } else {
+ PS2DBG(ErrorF("...failed. Wrong reset ack 0x%02x, 0x%02x\n", r[0], r[1]));
+ return FALSE;
+ }
+ }
+ PS2DBG(ErrorF("...failed\n"));
+ return FALSE;
+}
+
+static Bool
+ps2_synaptics_reset_passthrough(int fd)
+{
+ byte ack;
+
+ /* send reset */
+ ps2_putbyte_passthrough(fd, 0xff);
+ ps2_getbyte_passthrough(fd, &ack);
+ if (ack != 0xaa) {
+ PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0xaa\n", ack));
+ return FALSE;
+ }
+ ps2_getbyte_passthrough(fd, &ack);
+ if (ack != 0x00) {
+ PS2DBG(ErrorF("ps2_synaptics_reset_passthrough: ack was %02x not 0x00\n", ack));
+ return FALSE;
+ }
+
+ /* set defaults, turn on streaming, and enable the mouse */
+ return (ps2_putbyte_passthrough(fd, 0xf6) &&
+ ps2_putbyte_passthrough(fd, 0xea) &&
+ ps2_putbyte_passthrough(fd, 0xf4));
+}
+
+/*
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+static Bool
+ps2_synaptics_model_id(int fd, struct SynapticsHwInfo *synhw)
+{
+ byte mi[3];
+
+ PS2DBG(ErrorF("Read mode id...\n"));
+
+ 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])) {
+ synhw->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
+ PS2DBG(ErrorF("mode-id %06X\n", synhw->model_id));
+ PS2DBG(ErrorF("...done.\n"));
+ return TRUE;
+ }
+ PS2DBG(ErrorF("...failed.\n"));
+ return FALSE;
+}
+
+/*
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+static Bool
+ps2_synaptics_capability(int fd, struct SynapticsHwInfo *synhw)
+{
+ byte cap[3];
+
+ PS2DBG(ErrorF("Read capabilites...\n"));
+
+ synhw->capabilities = 0;
+ synhw->ext_cap = 0;
+ if (ps2_send_cmd(fd, SYN_QUE_CAPABILITIES) &&
+ ps2_getbyte(fd, &cap[0]) &&
+ ps2_getbyte(fd, &cap[1]) &&
+ ps2_getbyte(fd, &cap[2])) {
+ synhw->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ PS2DBG(ErrorF("capabilities %06X\n", synhw->capabilities));
+ if (SYN_CAP_VALID(*synhw)) {
+ if (SYN_EXT_CAP_REQUESTS(*synhw)) {
+ if (ps2_send_cmd(fd, SYN_QUE_EXT_CAPAB) &&
+ ps2_getbyte(fd, &cap[0]) &&
+ ps2_getbyte(fd, &cap[1]) &&
+ ps2_getbyte(fd, &cap[2])) {
+ synhw->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ PS2DBG(ErrorF("ext-capability %06X\n", synhw->ext_cap));
+ } else {
+ PS2DBG(ErrorF("synaptics says, that it has extended-capabilities, "
+ "but I cannot read them."));
+ }
+ }
+ PS2DBG(ErrorF("...done.\n"));
+ return TRUE;
+ }
+ }
+ PS2DBG(ErrorF("...failed.\n"));
+ return FALSE;
+}
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static Bool
+ps2_synaptics_identify(int fd, struct SynapticsHwInfo *synhw)
+{
+ byte id[3];
+
+ PS2DBG(ErrorF("Identify Touchpad...\n"));
+
+ synhw->identity = 0;
+ if (ps2_send_cmd(fd, SYN_QUE_IDENTIFY) &&
+ ps2_getbyte(fd, &id[0]) &&
+ ps2_getbyte(fd, &id[1]) &&
+ ps2_getbyte(fd, &id[2])) {
+ synhw->identity = (id[0] << 16) | (id[1] << 8) | id[2];
+ PS2DBG(ErrorF("ident %06X\n", synhw->identity));
+ if (SYN_ID_IS_SYNAPTICS(*synhw)) {
+ PS2DBG(ErrorF("...done.\n"));
+ return TRUE;
+ }
+ }
+ PS2DBG(ErrorF("...failed.\n"));
+ return FALSE;
+}
+
+static Bool
+ps2_synaptics_enable_device(int fd)
+{
+ return ps2_putbyte(fd, PS2_CMD_ENABLE);
+}
+
+static Bool
+ps2_synaptics_disable_device(int fd)
+{
+ xf86FlushInput(fd);
+ return ps2_putbyte(fd, PS2_CMD_DISABLE);
+}
+
+static Bool
+ps2_query_is_synaptics(int fd)
+{
+ struct SynapticsHwInfo synhw;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (ps2_synaptics_disable_device(fd))
+ break;
+ }
+
+ xf86WaitForInput(fd, 20000);
+ xf86FlushInput(fd);
+ if (ps2_synaptics_identify(fd, &synhw)) {
+ return TRUE;
+ } else {
+ ErrorF("Query no Synaptics: %06X\n", synhw.identity);
+ return FALSE;
+ }
+}
+
+void
+ps2_print_ident(const struct SynapticsHwInfo *synhw)
+{
+ 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)));
+ if (SYN_CAP_MIDDLE_BUTTON(*synhw))
+ xf86Msg(X_PROBED, " -> middle button\n");
+ if (SYN_CAP_FOUR_BUTTON(*synhw))
+ 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");
+ }
+}
+
+
+static void
+PS2DeviceOnHook(LocalDevicePtr local, SynapticsSHM* para)
+{
+}
+
+static void
+PS2DeviceOffHook(LocalDevicePtr local)
+{
+ ps2_synaptics_reset(local->fd);
+ ps2_synaptics_enable_device(local->fd);
+}
+
+static Bool
+PS2QueryHardware(LocalDevicePtr local, struct SynapticsHwInfo *synhw)
+{
+ int mode;
+
+ /* is the synaptics touchpad active? */
+ if (!ps2_query_is_synaptics(local->fd))
+ return FALSE;
+
+ xf86Msg(X_PROBED, "%s synaptics touchpad found\n", local->name);
+
+ if (!ps2_synaptics_reset(local->fd))
+ xf86Msg(X_ERROR, "%s reset failed\n", local->name);
+
+ if (!ps2_synaptics_identify(local->fd, synhw))
+ return FALSE;
+
+ if (!ps2_synaptics_model_id(local->fd, synhw))
+ return FALSE;
+
+ if (!ps2_synaptics_capability(local->fd, synhw))
+ 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;
+ if (!ps2_synaptics_set_mode(local->fd, mode))
+ return FALSE;
+
+ /* Check to see if the host mouse supports a guest */
+ synhw->hasGuest = FALSE;
+ if (SYN_CAP_PASSTHROUGH(*synhw)) {
+ synhw->hasGuest = TRUE;
+
+ /* Enable the guest mouse. Set it to relative mode, three byte
+ * packets */
+
+ /* Disable the host to talk to the guest */
+ ps2_synaptics_disable_device(local->fd);
+ /* Reset it, set defaults, streaming and enable it */
+ if (!ps2_synaptics_reset_passthrough(local->fd)) {
+ synhw->hasGuest = FALSE;
+ }
+ }
+
+ ps2_synaptics_enable_device(local->fd);
+
+ ps2_print_ident(synhw);
+
+ return TRUE;
+}
+
+/*
+ * Decide if the current packet stored in priv->protoBuf is valid.
+ */
+static Bool
+ps2_packet_ok(struct SynapticsHwInfo *synhw, struct CommData *comm)
+{
+ unsigned char *buf = comm->protoBuf;
+ int newabs = SYN_MODEL_NEWABS(*synhw);
+
+ 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
+ps2_synaptics_get_packet(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
+ struct SynapticsProtocolOperations *proto_ops,
+ struct CommData *comm)
+{
+ int count = 0;
+ int c;
+ unsigned char u;
+
+ while ((c = XisbRead(comm->buffer)) >= 0) {
+ u = (unsigned char)c;
+
+ /* test if there is a reset sequence received */
+ if ((c == 0x00) && (comm->lastByte == 0xAA)) {
+ if (xf86WaitForInput(local->fd, 50000) == 0) {
+ DBG(7, ErrorF("Reset received\n"));
+ proto_ops->QueryHardware(local, synhw);
+ } else
+ DBG(3, ErrorF("faked reset received\n"));
+ }
+ comm->lastByte = u;
+
+ /* to avoid endless loops */
+ if (count++ > 30) {
+ ErrorF("Synaptics driver lost sync... got gigantic packet!\n");
+ return FALSE;
+ }
+
+ comm->protoBuf[comm->protoBufTail++] = u;
+
+ /* Check that we have a valid packet. If not, we are out of sync,
+ so we throw away the first byte in the packet.*/
+ if (comm->protoBufTail >= 6) {
+ if (!ps2_packet_ok(synhw, comm)) {
+ int i;
+ 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;
+ DBG(3, ErrorF("Synaptics synchronization lost too long -> reset touchpad.\n"));
+ proto_ops->QueryHardware(local, synhw); /* including a reset */
+ continue;
+ }
+ }
+ }
+
+ if (comm->protoBufTail >= 6) { /* Full packet received */
+ if (comm->outOfSync > 0) {
+ comm->outOfSync = 0;
+ DBG(4, ErrorF("Synaptics driver resynced.\n"));
+ }
+ comm->protoBufTail = 0;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+PS2ReadHwState(LocalDevicePtr local, struct SynapticsHwInfo *synhw,
+ struct SynapticsProtocolOperations *proto_ops,
+ struct CommData *comm, struct SynapticsHwState *hwRet)
+{
+ int newabs = SYN_MODEL_NEWABS(*synhw);
+ unsigned char *buf = comm->protoBuf;
+ struct SynapticsHwState *hw = &(comm->hwState);
+ int w, i;
+
+ if (!ps2_synaptics_get_packet(local, synhw, proto_ops, comm))
+ return FALSE;
+
+ /* Handle guest packets */
+ hw->guest_dx = hw->guest_dy = 0;
+ if (newabs && synhw->hasGuest) {
+ 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;
+
+ if (SYN_CAP_EXTENDED(*synhw)) {
+ if (SYN_CAP_MIDDLE_BUTTON(*synhw)) {
+ hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ }
+ if (SYN_CAP_FOUR_BUTTON(*synhw)) {
+ 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;
+ }
+ if (SYN_CAP_MULTI_BUTTON_NO(*synhw)) {
+ if ((buf[3] & 2) ? !hw->right : hw->right) {
+ switch (SYN_CAP_MULTI_BUTTON_NO(*synhw) & ~0x01) {
+ 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.
+ */
+ if (SYN_CAP_EXTENDED(*synhw)) {
+ if ((w >= 0) && (w <= 1)) {
+ w_ok = SYN_CAP_MULTIFINGER(*synhw);
+ } else if (w == 2) {
+ w_ok = SYN_MODEL_PEN(*synhw);
+ } else if ((w >= 4) && (w <= 15)) {
+ w_ok = SYN_CAP_PALMDETECT(*synhw);
+ }
+ }
+ 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;
+}
+
+static Bool
+PS2AutoDevProbe(LocalDevicePtr local)
+{
+ return FALSE;
+}
+
+struct SynapticsProtocolOperations psaux_proto_operations = {
+ PS2DeviceOnHook,
+ PS2DeviceOffHook,
+ PS2QueryHardware,
+ PS2ReadHwState,
+ PS2AutoDevProbe
+};