/* * Copyright © 2001 Stefan Gmeiner * Copyright © 2003 Neil Brown * Copyright © 2003-2005,2007 Peter Osterlund * * 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. * * 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. * * Authors: * Stefan Gmeiner (riddlebox@freesurf.ch) * Neil Brown (neilb@cse.unsw.edu.au) * Peter Osterlund (petero2@telia.com) */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "synproto.h" #include "synaptics.h" #include "synapticsstr.h" #include "ps2comm.h" #include /* Wait for the channel to go silent, which means we're in sync */ static void ALPS_sync(int fd) { byte buffer[64]; while (xf86WaitForInput(fd, 250000) > 0) { xf86ReadSerial(fd, &buffer, 64); } } /* * send the ALPS init sequence, ie 4 consecutive "disable"s before the "enable" * This "magic knock" is performed both for the trackpad and for the pointing * stick. Not all models have a pointing stick, but trying to initialize it * anyway doesn't seem to hurt. */ static void ALPS_initialize(int fd) { xf86FlushInput(fd); ps2_putbyte(fd, PS2_CMD_SET_DEFAULT); ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1); ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1); ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_ENABLE); ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1); ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1); ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_DISABLE); ps2_putbyte(fd, PS2_CMD_ENABLE); ALPS_sync(fd); } static Bool ALPSQueryHardware(InputInfoPtr pInfo) { ALPS_initialize(pInfo->fd); return TRUE; } static Bool ALPS_packet_ok(struct CommData *comm) { /* ALPS absolute mode packets start with 0b11111mrl */ if ((comm->protoBuf[0] & 0xf8) == 0xf8) return TRUE; return FALSE; } static Bool ALPS_get_packet(struct CommData *comm, InputInfoPtr pInfo) { int c; while ((c = XisbRead(comm->buffer)) >= 0) { unsigned char u = (unsigned char) c; comm->protoBuf[comm->protoBufTail++] = u; if (comm->protoBufTail == 3) { /* PS/2 packet received? */ if ((comm->protoBuf[0] & 0xc8) == 0x08) { comm->protoBufTail = 0; return TRUE; } } if (comm->protoBufTail >= 6) { /* Full packet received */ comm->protoBufTail = 0; if (ALPS_packet_ok(comm)) return TRUE; while ((c = XisbRead(comm->buffer)) >= 0); /* If packet is invalid, re-sync */ } } return FALSE; } /* * ALPS abolute Mode * byte 0: 1 1 1 1 1 mid0 rig0 lef0 * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 * byte 2: 0 x10 x9 x8 x7 up1 fin ges * byte 3: 0 y9 y8 y7 1 mid1 rig1 lef1 * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 * * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad. * We just 'or' them together for now. * * The touchpad on an 'Acer Aspire' has 4 buttons: * left,right,up,down. * This device always sets {mid,rig,lef}0 to 1 and * reflects left,right,down,up in lef1,rig1,mid1,up1. */ static void ALPS_process_packet(unsigned char *packet, struct SynapticsHwState *hw) { int x = 0, y = 0, z = 0; int left = 0, right = 0, middle = 0; int i; hw->millis = GetTimeInMillis(); x = (packet[1] & 0x7f) | ((packet[2] & 0x78) << (7 - 3)); y = (packet[4] & 0x7f) | ((packet[3] & 0x70) << (7 - 4)); z = packet[5]; if (z == 127) { /* DualPoint stick is relative, not absolute */ hw->left = packet[3] & 1; hw->right = (packet[3] >> 1) & 1; return; } /* 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 (z > 0) { hw->x = x; hw->y = y; } hw->z = z; hw->numFingers = (z > 0) ? 1 : 0; hw->fingerWidth = 5; left |= (packet[2]) & 1; left |= (packet[3]) & 1; right |= (packet[3] >> 1) & 1; if (packet[0] == 0xff) { int back = (packet[3] >> 2) & 1; int forward = (packet[2] >> 2) & 1; if (back && forward) { middle = 1; back = 0; forward = 0; } hw->down = back; hw->up = forward; } else { left |= (packet[0]) & 1; right |= (packet[0] >> 1) & 1; middle |= (packet[0] >> 2) & 1; middle |= (packet[3] >> 2) & 1; } hw->left = left; hw->right = right; hw->middle = middle; } static Bool ALPSReadHwState(InputInfoPtr pInfo, struct CommData *comm, struct SynapticsHwState *hwRet) { unsigned char *buf = comm->protoBuf; struct SynapticsHwState *hw = comm->hwState; if (!ALPS_get_packet(comm, pInfo)) return FALSE; ALPS_process_packet(buf, hw); SynapticsCopyHwState(hwRet, hw); return TRUE; } struct SynapticsProtocolOperations alps_proto_operations = { NULL, NULL, ALPSQueryHardware, ALPSReadHwState, NULL, NULL };