text: /*
   * 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 <xorg-server.h>
  #include "synaptics.h"
  #include "synproto.h"
  #include "synapticsstr.h"
  #include "ps2comm.h"
  #include <xf86.h>
  
  /* 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
  };
  