text: /*
   * xf86Aiptek
   *
   * This driver assumes Linux USB/HID support, available for USB devices.
   * 
   * Version 0.0, 1-Jan-2003, Bryan W. Headley
   * 
   * Copyright 2003 by Bryan W. Headley. <bwheadley@earthlink.net>
   *
   * Lineage: This driver is based on both the xf86HyperPen and xf86Wacom tablet
   *          drivers.
   *
   *      xf86HyperPen -- modified from xf86Summa (c) 1996 Steven Lang
   *          (c) 2000 Roland Jansen
   *          (c) 2000 Christian Herzog (button & 19200 baud support)
   *
   *      xf86Wacom -- (c) 1995-2001 Frederic Lepied
   *
   *
   * 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 Bryan W. Headley not be used in 
   * advertising or publicity pertaining to distribution of the software 
   * without specific, written prior permission.  Bryan W. Headley makes no 
   * representations about the suitability of this software for any purpose.  
   * It is provided "as is" without express or implied warranty.
   *
   * BRYAN W. HEADLEY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
   * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
   * EVENT SHALL BRYAN W. HEADLEY 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 ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
   * PERFORMANCE OF THIS SOFTWARE.
   */
  /* $XFree86: xc/programs/Xserver/hw/xfree86/input/aiptek/xf86Aiptek.c,v 1.1tsi Exp $ */
  
  /*
   *
   * Section "InputDevice"
   *      Identifier  "stylus"
   *      Driver      "aiptek"
   *      Option      "Device"        "pathname" {/dev/input/event0}
   *      Option      "Type"          "string" {stylus|cursor|eraser}
   *      Option      "Mode"          "string" {absolute|relative}
   *      Option      "Cursor"        "string" {stylus|puck}
   *      Option      "USB"           "bool"   {on|off}
   *      Option      "ScreenNo"      "int"
   *      Option      "KeepShape"     "bool"   {on|off}
   *
   *      # The tablet reports top-right as 0 for the given coordinate
   *      # to bottom-left as num (value dependent on tablet.)
   *      # If you choose to invert X, Y or both, the bottom-left coordinate
   *      # is reported as 0.
   *      Option      "InvX"          "bool"   {on|off}
   *      Option      "InvY"          "bool"   {on|off}
   *
   *      # XSize/YSize/XOffset/YOffset allow you to specify an active
   *      # area within yout tablet.
   *
   *      Option      "XSize"         "int"
   *      Option      "YSize"         "int"
   *      
   *      Option      "XTop"          "int"
   *      Option      "YTop"          "int"
   *      Option      "XBottom"       "int"
   *      Option      "YBottom"       "int"
   *      Option      "XOffset"       "int"
   *      Option      "YOffset"       "int"
   *
   *      Option      "XMax"          "int"
   *      Option      "YMax"          "int"
   *      Option      "ZMax"          "int"
   *      Option      "ZMin"          "int"
   *
   *      Option      "XThreshold"  "int"
   *      Option      "YThreshold"  "int"
   *      Option      "ZThreshold"  "int"
   *
   *      Option      "Pressure"      "Soft|Hard|Linear"  defaults to Linear
   *
   *      Option      "alwayscore"  "bool"   {on|off}
   *      Option      "debuglevel"  "int"
   *      Option      "HistorySize" "int"
   * EndSection
   *
   *  Commentary:
   *  1.  Identifier: what you name your input device is not too
   *      significant. 
   *      but what it infers is that you can have a driver with
   *      a name of "stylus" (whose type would be 'stylus') and
   *      another one with identifier "cursor" (whose type would be
   *      'cursor') that both would be driver by the same aiptek
   *      driver. Note though that the identifier keyword has
   *      no devicetype connotations: you can identify your input
   *      device as "zzz", 
   *
   */
  
  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif
  
  #include "xf86Aiptek.h"
  
  static const char identification[] = "$Identification: 0 $";
  static InputDriverPtr aiptekDrv;
  static int debug_level = INI_DEBUG_LEVEL;
  
  _X_EXPORT InputDriverRec AIPTEK =
  {
      1,                  /* driver version */
      "aiptek",           /* driver name */
      NULL,               /* identify */
      xf86AiptekInit,     /* pre-init */
      xf86AiptekUninit,   /* un-init */
      NULL,               /* module */
      0                   /* ref count */
  };
  
  /*
   * Function/Macro keys variables.
   *
   * This is a list of X keystrokes the macro keys can send.
   */
  static KeySym aiptek_map[] =
  {
      /* 0x00 .. 0x07 */
      NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,
      /* 0x08 .. 0x0f */
      XK_F1,  XK_F2,  XK_F3,  XK_F4,  XK_F5,  XK_F6,  XK_F7,  XK_F8,
      /* 0x10 .. 0x17 */
      XK_F9,  XK_F10, XK_F11, XK_F12, XK_F13, XK_F14, XK_F15, XK_F16,
      /* 0x18 .. 0x1f */
      XK_F17, XK_F18, XK_F19, XK_F20, XK_F21, XK_F22, XK_F23, XK_F24,
      /* 0x20 .. 0x27 */
      XK_F25, XK_F26, XK_F27, XK_F28, XK_F29, XK_F30, XK_F31, XK_F32
  };
  
  /*
   * This is the map of Linux Event Input system keystrokes sent for
   * the macro keys. There are discrepancies in the mappings, so for example,
   * if we wanted to implement full macro key-to-string conversion in the
   * Linux driver, we'd have to accept 1-to-many keyboard events, several of
   * whom would not have the same encoding. For this reason, we're biting
   * the bullet now & implementing a simple lookup/translation scheme.
   * A simple 'KEY_F1 = XK_F1' layout wouldn't work, because X wants an
   * offset into the KeySym array above, and it'll look up that this means 
   * XK_whatever...
   */
  
  static int linux_inputDevice_keyMap[] =
  {
      KEY_F1,  KEY_F2,  KEY_F3,  KEY_F4,  KEY_F5,  KEY_F6,  KEY_F7,  KEY_F8,
      KEY_F9,  KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16,
      KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24, 
      KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, KEY_FRONT, KEY_COPY,
      KEY_OPEN, KEY_PASTE
  };
  
  /* minKeyCode = 8 because this is the min legal key code */
  static KeySymsRec keysyms =
  {
    /* map        minKeyCode  maxKC   width */
    aiptek_map,   8,          0x27,   1
  };
  
  static const char *default_options[] =
  {
      "BaudRate",     "9600",
      "StopBits",     "1",
      "DataBits",     "8",
      "Parity",       "None",
      "VMin",         "1",
      "Vtime",        "10",
      "FlowControl",  "Xoff",
      NULL
  };
  
  /*
   * xf86AiptekConvert
   * Convert valuators to X and Y. We deal with multiple X screens, adjusting
   * for xTop/xBottom/yTop/yBottom (or xSize/ySize).
   */
  static Bool
  xf86AiptekConvert(LocalDevicePtr local,
                    int            first,
                    int            num,
                    int            v0,
                    int            v1,
                    int            v2,
                    int            v3,
                    int            v4,
                    int            v5,
                    int*           x,
                    int*           y)
  {
      AiptekDevicePtr device = (AiptekDevicePtr) local->private;
      int  xSize, ySize;
      int  width, height;
  
      DBG(6, ErrorF("xf86AiptekConvert\n"));
      xf86Msg(X_CONFIG, " xf86AiptekConvert(), with: first=%d, num=%d, v0=%d, "
              "v1=%d, v2=%d, v3=%d,, v4=%d, v5=%d, x=%d, y=%d\n",
              first, num, v0, v1, v2, v3, v4, v5, *x, *y);
  
      if (first != 0 || num == 1)
      {
          return FALSE;
      }
  
      xSize = device->xBottom - device->xTop;
      ySize = device->yBottom - device->yTop;
  
      width  = screenInfo.screens[device->screenNo]->width;
      height = screenInfo.screens[device->screenNo]->height;
  
      *x = (v0 * width)  / xSize;
      *y = (v1 * height) / ySize;
  
      /* Deal with coordinate inversion */
      if ( device->flags & INVX_FLAG)
      {
          *x = width - *x;
      }
      if ( device->flags & INVY_FLAG)
      {
          *y = height - *y;
      }
  
      /* Normalize the adjusted sizes. */
      if (*x < 0)
      {
          *x = 0;
      }
      if (*x > width)
      {
          *x = width;
      }
  
      if (*y < 0)
      {
          *y = 0;
      }
      if (*y > height)
      {
          *y = height;
      }
  
      if (device->screenNo != 0)
      {
          xf86XInputSetScreen(local, device->screenNo, *x, *y);
      }
      xf86Msg(X_CONFIG, ": xf86AiptekConvert() exits, with: x=%d, y=%d\n",
              *x, *y);
  
      return TRUE;
  }
  
  /*
   * xf86AiptekReverseConvert
   * Convert X and Y to valuators.
   */
  static Bool
  xf86AiptekReverseConvert(LocalDevicePtr local,
                           int            x,
                           int            y,
                           int*           valuators)
  {
      AiptekDevicePtr device = (AiptekDevicePtr) local->private;
      int    xSize, ySize;
  
      xf86Msg(X_CONFIG, ": xf86AiptekReverseConvert(), with: x=%d, y=%d, "
              "valuators[0]=%d, valuators[1]=%d\n",
              x, y, valuators[0], valuators[1] );
  
      /*
       * Adjust by tablet ratio
       */
      xSize = device->xBottom - device->xTop;
      ySize = device->yBottom - device->yTop;
  
      valuators[0] = (x*xSize) / screenInfo.screens[device->screenNo]->width;
      valuators[1] = (y*ySize) / screenInfo.screens[device->screenNo]->height;
  
      DBG(6, ErrorF("converted x,y (%d, %d) to (%d, %d)\n",
                      x, y, valuators[0], valuators[1] ));
  
      if (device->screenNo != 0)
      {
          xf86XInputSetScreen(local,device->screenNo,valuators[0], valuators[1]);
      }
      xf86Msg(X_CONFIG, ": xf86AiptekReverseConvert() exits, with: "
              "valuators[0]=%d, valuators[1]=%d\n",
              valuators[0], valuators[1] );
  
      return TRUE;
  }
  
  /**********************************************************************
   * xf86AiptekSendEvents
   *  Send events according to the device state.
   */
  static void
  xf86AiptekSendEvents(LocalDevicePtr local, int r_z)
  {
      AiptekDevicePtr device = (AiptekDevicePtr) local->private;
      AiptekCommonPtr common = device->common;
  
      int bCorePointer, bAbsolute;
      int x, y, z, xTilt, yTilt;
  
      if ((DEVICE_ID(device->flags) != common->currentValues.eventType))
      {
          DBG(7,ErrorF("xf86AiptekSendEvents: not the same device type (%u,%u)\n",
             DEVICE_ID(device->flags), common->currentValues.eventType));
          return;
      }
  
      bAbsolute    = (device->flags & ABSOLUTE_FLAG);
      bCorePointer = xf86IsCorePointer(local->dev);
  
      /*
       * Normalize X and Y coordinates. This includes dealing
       * with absolute/relative coordinate mode.
       */
      if (bAbsolute)
      {
          x = common->currentValues.x;
          y = common->currentValues.y;
          z = r_z;
          xTilt = common->currentValues.xTilt;
          yTilt = common->currentValues.yTilt;
      }
      else
      {
          x = common->currentValues.x - common->previousValues.x;
          y = common->currentValues.y - common->previousValues.y;
          z = r_z - common->previousValues.z;
          xTilt = common->currentValues.xTilt - common->previousValues.xTilt;
          yTilt = common->currentValues.yTilt - common->previousValues.yTilt;
      }
  
      /* Translate coordinates according to Top and Bottom points.
       */
      if (x > device->xBottom) {
          x = device->xBottom;
      }
  
      if (y > device->yBottom) {
          y = device->yBottom;
      }
  
      if (device->xTop > 0) {
          DBG(10, ErrorF("Adjusting x, with xTop=%d\n", device->xTop));
          x -= device->xTop;
      }
  
      if (device->yTop > 0) {
          DBG(10, ErrorF("Adjusting y, with yTop=%d\n", device->yTop));
          y -= device->yTop;
      }
  
      if (x < 0) {
          x = 0;
      }
      if (y < 0) {
          y = 0;
      }
  
      /* Deal with pressure min..max, which differs from threshold. */
      if (z < device->zMin) {
          z = 0;
      }
  
      if (z > device->zMax) {
          z = device->zMax;
      }
  
      /*
       * First, handle the macro keys.
       */
      if (common->currentValues.macroKey != VALUE_NA)
      {
          int i;
  
          /* This is a little silly, but: The Linux Event Input
           * system uses a slightly different keymap than does X 
           * (it also has more keys defined). So, we have to go
           * through a translation process. It's made sillier than
           * required because X wants an offset to it's KeySym table,
           * rather than an event key -- it'll do it's own lookup.
           * It DOES support arbitrary ordering of key events, and
           * partial keyboard matrices, so that speaks in favor of this
           * scheme.
           */
          for (i = 0; 
               i < sizeof(linux_inputDevice_keyMap)/
                   sizeof(linux_inputDevice_keyMap[0]);
               ++i)
          {
              if (linux_inputDevice_keyMap[0]==common->currentValues.macroKey)
              {
                  break;
              }
          }
  
          /* First available Keycode begins at 8 => macro+7.
           * It's pervasive throughout the Xinput drivers, and
           * no, I don't know why they purposively waste the first 8
           * positions of the KeySym map...
           */
  
          /* Keyboard 'make' (press) event */
          xf86PostKeyEvent(local->dev, i+7, TRUE,
                           bAbsolute, 0, 5,
                           x, y, common->currentValues.button, xTilt, yTilt);
          /* Keyboard 'break' (depress) event */
          xf86PostKeyEvent(local->dev, i+7, FALSE,
                           bAbsolute, 0, 5,
                           x, y, common->currentValues.button, xTilt, yTilt);
      }
  
      /* As the coordinates are ready, we can send events to X */
      if (common->currentValues.proximity)
      {
          if (!common->previousValues.proximity)
          {
              if (!bCorePointer)
              {
                  xf86PostProximityEvent(local->dev, 1, 0, 5,
                      x, y, z, xTilt, yTilt);
              }
          }
  
          if ((bAbsolute &&
               (common->previousValues.x != common->currentValues.x ||
                common->previousValues.y != common->currentValues.y ||
                common->previousValues.z != common->currentValues.z)) ||
              (!bAbsolute && 
               (common->currentValues.x || common->currentValues.y)))
          {
              if (bAbsolute || common->previousValues.proximity)
              {
                  xf86PostMotionEvent(local->dev, bAbsolute, 0, 5,
                          x, y, z, xTilt, yTilt);
              }
          }
         
          if (common->previousValues.button != common->currentValues.button)
          {
              int delta;
              delta = common->currentValues.button ^ common->previousValues.button;
              while(delta)
              {
                  int id;
                  id = ffs(delta);
                  delta &= ~(1 << (id-1));
                  xf86PostButtonEvent(local->dev, bAbsolute, id,
                          (common->currentValues.button & (1<<(id-1))), 0, 5,
                          x, y, z, xTilt, yTilt);
              }
          }
      }
      else
      {
          if (!bCorePointer)
          {
              if (common->previousValues.proximity)
              {
                  xf86PostProximityEvent(local->dev, 0, 0, 5, x, y, z,
                          xTilt, yTilt);
              }
          }
          common->previousValues.proximity = 0;
      }
  }
  
  /*
   ***************************************************************************
   * xf86AiptekHIDReadInput --
   *    Read the new events from the device, and enqueue them.
   */
  static void
  xf86AiptekHIDReadInput(LocalDevicePtr local)
  {
      AiptekDevicePtr     device = (AiptekDevicePtr) local->private;
      AiptekCommonPtr     common = device->common;
  
      ssize_t             len;
      int                 i;
      struct input_event* event;
      char                eventbuf[sizeof(struct input_event) * MAX_EVENTS];
      int                 eventsInMessage;
      double              d_z;
      double              d_zCapacity;
  
      SYSCALL(len = read(local->fd, eventbuf, sizeof(eventbuf)));
  
      if (len <= 0)
      {
          ErrorF("Error reading Aiptek tablet: %s\n", strerror(errno));
          return;
      }
  
      eventsInMessage = 0;
      for (event=(struct input_event *)(eventbuf);
           event<(struct input_event *)(eventbuf+len);
           ++event)
      {
          /*
           * Unprocessed events:
           * ABS_RZ - rotate stylus
           * ABS_DISTANCE - unknown
           * ABS_THROTTLE - unknown
           * ABS_WHEEL - we have no wheel
           * REL_WHEEL - we have no wheel
           *
           * Synthesized events
           * ABS_X_TILT - The aiptek tablet does not report these,
           * ABS_Y_TILT - but the Linux kernel driver sends synthetic values.
           */
          switch (event->type)
          {
              case EV_ABS:
              {
                  switch (event->code)
                  {
                      case ABS_X:
                      {
                          ++eventsInMessage;
                          common->currentValues.x = event->value;
                      }
                      break;
  
                      case ABS_Y:
                      {
                          ++eventsInMessage;
                          common->currentValues.y = event->value;
                      }
                      break;
  
                      case ABS_PRESSURE:
                      {
                          ++eventsInMessage;
                          common->currentValues.z = event->value;
                      }
                      break;
  
                      case ABS_TILT_X:
                      case ABS_RZ:
                      {
                          ++eventsInMessage;
                          common->currentValues.xTilt = event->value;
                      }
                      break;
  
                      case ABS_TILT_Y:
                      {
                          ++eventsInMessage;
                          common->currentValues.yTilt = event->value;
                      }
                      break;
  
                      case ABS_DISTANCE:
                      {
                          ++eventsInMessage;
                          common->currentValues.distance = event->value;
                      }
                      break;
  
                      case ABS_WHEEL:
                      case ABS_THROTTLE:
                      {
                          ++eventsInMessage;
                          common->currentValues.wheel = event->value;
                      }
                      break;
  
                      case ABS_MISC:
                      {
                          /* We have an agreement with the
                           * Linux Aiptek HID driver to send
                           * the proximity bit through ABS_MISC.
                           * We do this solely if proximity is
                           * being reported through the Stylus tool;
                           * else, if mouse, we'll get proximity through
                           * REL_MISC.
                           */
                          ++eventsInMessage;
                          common->currentValues.proximity = 
                              (event->value > 0 ? 1 : 0);
                      }
                      break;
                  }
              }
              break; /* EV_ABS */
  
              case EV_REL:
              {
                  switch (event->code)
                  {
                      case REL_X:
                      {
                          /* Normalize all relative events into absolute
                           * coordinates.
                           */
                          ++eventsInMessage;
                          common->currentValues.x = 
                              common->previousValues.x + event->value;
                      }
                      break;
  
                      case REL_Y:
                      {
                          /* Normalize all relative events into absolute
                           * coordinates.
                           */
                          ++eventsInMessage;
                          common->currentValues.y =
                              common->previousValues.y + event->value;
                      }
                      break;
  
                      case REL_WHEEL:
                      {
                          /* Normalize all relative events into absolute
                           * coordinates.
                           */
                          ++eventsInMessage;
                          common->currentValues.wheel =
                              common->previousValues.wheel + event->value;
                      }
  
                      case REL_MISC:
                      {
                          /* We have an agreement with the
                           * Linux Aiptek HID driver to send
                           * the proximity bit through REL_MISC.
                           * We do this solely if proximity is
                           * being reported through the Mouse tool;
                           * else, if stylus, we'll get proximity through
                           * ABS_MISC.
                           */
                          ++eventsInMessage;
                          common->currentValues.proximity = 
                              (event->value > 0 ? 1 : 0);
                      }
                      break;
                  }
              }
              break; /* EV_REL */
  
              case EV_KEY:
              {
                  switch (event->code)
                  {
                      /*
                       * Events begun with a BTN_TOOL_PEN, PENCIL,
                       * BRUSH or AIRBRUSH indicate that they are
                       * destined for the STYLUS device.
                       *
                       * This should probably change, and we should
                       * have devices for each type. We'll address that
                       * later.
                       */
                      case BTN_TOOL_PEN:
                      case BTN_TOOL_PENCIL:
                      case BTN_TOOL_BRUSH:
                      case BTN_TOOL_AIRBRUSH:
                      {
                          ++eventsInMessage;
                          common->currentValues.eventType = STYLUS_ID;
                      }
                      break;
  
                      /*
                       * Events begun with a BTN_TOOL_RUBBER indicate
                       * that they are destined for the ERASER device.
                       */
                      case BTN_TOOL_RUBBER:
                      {
                          ++eventsInMessage;
                          common->currentValues.eventType = ERASER_ID;
                      }
                      break;
  
                      /*
                       * A TOOL_LENS would be for a true PUCK/CURSOR.
                       * Aiptek instead gives us a mouse, that we can pretend
                       * is a puck.
                       */
                      case BTN_TOOL_MOUSE:
                      case BTN_TOOL_LENS:
                      {
                          ++eventsInMessage;
                          common->currentValues.eventType = CURSOR_ID;
                      }
                      break;
  
                      /*
                       * Normal button handling: TOUCH, STYLUS and
                       * STYLUS2 all buttons that we'll report to X
                       * as normal buttons.
                       */
                      case BTN_TOUCH:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_TOUCH;
                      }
                      break;
  
                      case BTN_STYLUS:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_STYLUS;
                      }
                      break;
  
                      case BTN_STYLUS2:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_STYLUS2;
                      }
                      break;
  
                      /*
                       * Normal Mouse button handling: LEFT, RIGHT and
                       * MIDDLE all buttons that we'll report to X
                       * as normal buttons. Note that the damned things
                       * re-use the same bitmasks as the Stylus buttons,
                       * above.
                       */
                      case BTN_LEFT:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_LEFT;
                      }
                      break;
  
                      case BTN_MIDDLE:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_MIDDLE;
                      }
                      break;
  
                      case BTN_RIGHT:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_RIGHT;
                      }
                      break;
  
                      case BTN_SIDE:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_SIDE_BTN;
                      }
                      break;
  
                      case BTN_EXTRA:
                      {
                          ++eventsInMessage;
                          common->currentValues.button |= 
                              (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_EXTRA_BTN;
                      }
                      break;
  
                      /*
                       * Any other EV_KEY event is assumed to be
                       * the pressing of a macro key from the tablet.
                       */
                      default:
                      {
                          ++eventsInMessage;
                          common->currentValues.macroKey = event->value;
                      }
                      break;
                  }
              }
              break; /* EV_KEY */
  
          } /* switch event->type */
  
          /* We have two potential event terminators. EV_MSC was used
           * by (unwritten) convention to indicate the end-of-report.
           * Problem is, EV_MSC is supposed to actually report data,
           * so a new event type, EV_SYN, was created in Linux 2.5.x
           * expressively for this purpose.
           *
           * Theoretically, if EV_SYN is defined, I should only terminate
           * the population of device->currentValues struct IFF I receive
           * that event. The fact of the matter is, the EV_MSC is assumed
           * to be an ugliness that will take some time to be deprecated.
           * For the nonce, we'll support both. But, if you have a tablet
           * that's actually supplying something interesting with EV_MSC,
           * this is obviously some code that requires modifications.
           */
  #ifndef EV_SYN
          if (event->type != EV_MSC)
  #else
          if (event->type != EV_MSC && event->type != EV_SYN)
  #endif
          {
              continue;
          }
  
          /*
           * We've seen EV_MSCs in the incoming data trail with no
           * other message types in-between. We use 'eventsInMessage'
           * to count all 'real' messages in-between. If there were none,
           * do NOT copy common->currentValues to common->previousValues
           * (as this will make the jitter filter useless). Just go and
           * read the subsequent events.
           */
          if (eventsInMessage == 0)
          {
              continue;
          }
          eventsInMessage = 0;
  
          /* 
           * This filter throws out reports that do not meet minimum threshold
           * requirements for movement along that axis.
           *
           * Presently, we discard the entire report if any dimension of the
           * currentValues struct does not meet it's minimum threshold.
           *
           * Also, we only do the comparison IFF a threshold has been set
           * for that given dimension.
           */
          if ((device->xThreshold > 1 && 
                  ABS(common->currentValues.x - common->previousValues.x)
                      <= device->xThreshold) ||
              (device->yThreshold > 1 &&
                  ABS(common->currentValues.y - common->previousValues.y)
                      <= device->yThreshold) ||
              (device->zThreshold > 1 &&
                  ABS(common->currentValues.z - common->previousValues.z)
                      <= device->zThreshold) ||
              (device->xTiltThreshold > 1 &&
                  ABS(common->currentValues.xTilt - common->previousValues.xTilt)
                      <= device->xTiltThreshold) ||
              (device->yTiltThreshold > 1 &&
                  ABS(common->currentValues.yTilt - common->previousValues.yTilt)
                      <= device->yTiltThreshold))
          {
              DBG(10, ErrorF("Event Filtered Out by Thresholds\n"));
              continue;
          }
  
          /*
           * If this report somehow has exactly the same readings as the
           * previous report for all dimensions, throw the report out.
           */
          if ((common->currentValues.x == common->previousValues.x) &&
              (common->currentValues.y == common->previousValues.y) &&
              (common->currentValues.z == common->previousValues.z) &&
              (common->currentValues.proximity == 
                                           common->previousValues.proximity) &&
              (common->currentValues.button == 
                                           common->previousValues.button) &&
              (common->currentValues.macroKey ==
                                           common->previousValues.macroKey))
          {
              DBG(10, ErrorF("Event Filtered Out\n"));
              continue;
          }
  
          /*
           * We have three different methods by which we report pressure, Z.
           * One is to use linear values from 0 to common->zCapacity. The
           * other two, SOFT_SMOOTH and HARD_SMOOTH, use different
           * algorithms to 'smooth out' the values.
           */
          d_z         = (double)common->currentValues.z;
          d_zCapacity = (double)common->zCapacity;
  
          switch (device->zMode)
          {
              case VALUE_NA:
              case PRESSURE_MODE_LINEAR:
              {
                  /* Leave Z alone. */
              }
              break;
  
              case PRESSURE_MODE_SOFT_SMOOTH:
              {
                  d_z = (d_z * d_z / d_zCapacity)+ 0.5;
              }
              break;
  
              case PRESSURE_MODE_HARD_SMOOTH:
              {
                  d_z = (d_zCapacity * sqrt( d_z / d_zCapacity)) + 0.5;
              }
              break;
          }
  
          /* Dispatch events to all of our configured devices. */
          for (i=0; i < common->numDevices; ++i)
          {
              AiptekDevicePtr dev = common->deviceArray[i]->private;
              int             id;
  
              id  = DEVICE_ID (dev->flags);
  
              /* Find the device the current events are meant for */
              if (id == common->currentValues.eventType)
              {
                  /* We left 'z' alone during smoothing, so send up
                   * perturbed value outside of the struct
                   */
                  xf86AiptekSendEvents(common->deviceArray[i], (int) d_z);
              }
          }
  
          /*
           * Copy the values just processed into the previousValues struct,
           * so we can check for 'jittering' in the subsequent report.
           */
          common->previousValues.eventType = common->currentValues.eventType;
          common->previousValues.x         = common->currentValues.x;
          common->previousValues.y         = common->currentValues.y;
          common->previousValues.z         = common->currentValues.z;
          common->previousValues.proximity = common->currentValues.proximity;
          common->previousValues.button    = common->currentValues.button;
          common->previousValues.macroKey  = common->currentValues.macroKey;
          common->previousValues.xTilt     = common->currentValues.xTilt;
          common->previousValues.yTilt     = common->currentValues.yTilt;
          common->previousValues.distance  = common->currentValues.distance;
          common->previousValues.wheel     = common->currentValues.wheel;
  
          common->currentValues.macroKey   = VALUE_NA;
      }
  }
  
  /*
   ***************************************************************************
   *
   * xf86AiptekHIDOpen --
   *
   ***************************************************************************
   */
  static Bool
  xf86AiptekHIDOpen(LocalDevicePtr local)
  {
      AiptekDevicePtr device = (AiptekDevicePtr)local->private;
      AiptekCommonPtr common = device->common;
      char            name[256] = "Unknown";
      int             abs[5];
      unsigned long   bit[EV_MAX][NBITS(KEY_MAX)];
      int             i, j;
      int             err = 0;
      int             version;
  
      local->fd = xf86OpenSerial(local->options);
      if (local->fd == -1)
      {
          ErrorF("xf86AiptekHIDOpen Error opening %s : %s\n", common->deviceName, strerror(errno));
          return !Success;
      }
  
      ioctl(local->fd, EVIOCGNAME(sizeof(name)), name);
      ErrorF("%s HID Device name: \"%s\"\n", XCONFIG_PROBED, name);
  
      ioctl(local->fd, EVIOCGVERSION, &version);
      ErrorF("%s HID Driver Version: %d.%d.%d\n", XCONFIG_PROBED,
              version>>16, (version>>8) & 0xff, version & 0xff);
  
      ErrorF("%s HID Driver knows it has %d devices configured\n", XCONFIG_PROBED,
              common->numDevices);
      ErrorF("%s HID Driver is using %d as the fd\n", XCONFIG_PROBED, local->fd);
  
      for (i = 0; i < common->numDevices; ++i)
      {
          common->deviceArray[i]->read_input = xf86AiptekHIDReadInput;
          common->deviceArray[i]->fd = local->fd;
          common->deviceArray[i]->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
      }
      common->open = xf86AiptekHIDOpen;
  
      memset(bit, 0, sizeof(bit));
      ioctl(local->fd, EVIOCGBIT(0, EV_MAX), bit[0]);
  
      for (i = 0; i < EV_MAX; ++i)
      {
          if (TEST_BIT(i, bit[0]))
          {
              ioctl(local->fd, EVIOCGBIT(i, KEY_MAX), bit[i]);
              for (j = 0; j < KEY_MAX; ++j)
              {
                  if (TEST_BIT(j, bit[i]))
                  {
                      if (i == EV_ABS)
                      {
                          ioctl(local->fd, EVIOCGABS(j), abs);
                          switch (j) 
                          {
                              case ABS_X:
                              {
                                  ErrorF("From ioctl() xCapacity=%d\n", abs[2]);
                                  common->xCapacity = abs[2];
                              }
                              break;
  
                              case ABS_Y:
                              {
                                  ErrorF("From ioctl() yCapacity=%d\n", abs[2]);
                                  common->yCapacity = abs[2];
                              }
                              break;
  
                              case ABS_Z:
                              {
                                  ErrorF("From ioctl() zCapacity=%d\n", abs[2]);
                                  common->zCapacity = abs[2];
                              }
                              break;
                          }
                      }
                  }
              }
          }
      }
  
      if (err < 0)
      {
          ErrorF("xf86AiptekHIDOpen ERROR: %d\n", err);
          SYSCALL(close(local->fd));
          return !Success;
      }
  
      return Success;
  }
  
  /*
   * xf86AiptekControlProc
   */
  static void
  xf86AiptekControlProc(DeviceIntPtr device, PtrCtrl *ctrl)
  {
      DBG(2, ErrorF("xf86AiptekControlProc\n"));
  }
  
  /*
  ** xf86AiptekOpen
   * Open and initialize the tablet, as well as probe for any needed data.
   * (This is TTY style open)
   */
  
  static Bool
  xf86AiptekOpen(LocalDevicePtr local)
  {
      AiptekDevicePtr device = (AiptekDevicePtr)local->private;
      AiptekCommonPtr common = device->common;
      int err, version;
  
      DBG(1, ErrorF("Opening %s\n", common->deviceName));
  
      local->fd = xf86OpenSerial(local->options);
      if (local->fd < 0)
      {
          ErrorF("Error opening %s: %s\n", common->deviceName, strerror(errno));
          return !Success;
      }
  
      DBG(1, ErrorF("Testing USB\n"));
  
      SYSCALL(err = ioctl(local->fd, EVIOCGVERSION, &version));
      if (!err)
      {
          int j;
  
          SYSCALL(close(local->fd));
  
          for(j=0; j<common->numDevices; ++j)
          {
              common->deviceArray[j]->read_input=xf86AiptekHIDReadInput;
          }
          common->open=xf86AiptekHIDOpen;
  
          return xf86AiptekHIDOpen(local);
      }
  
      /* We do not support TTY mode, so just exit angry. */
      return !Success;
  }
  
  /*
   * xf86AiptekOpenDevice
   * Opens and initializes the device driver.
   */
  static int
  xf86AiptekOpenDevice(DeviceIntPtr pDriver)
  {
      LocalDevicePtr    local  = (LocalDevicePtr)pDriver->public.devicePrivate;
      AiptekDevicePtr   device = (AiptekDevicePtr)PRIVATE(pDriver);
      AiptekCommonPtr   common = device->common;
      double            tabletRatio, screenRatio;
      double            xFactor, yFactor;
      int               gap, loop;
  
      DBG(2, ErrorF("In xf86AiptekOpenDevice, with fd=%d\n", local->fd));
  
      if (local->fd < 0)
      {
          if (common->initNumber > 2 ||
              device->initNumber == common->initNumber)
          {
              if (common->open(local) != Success)
              {
                  if (local->fd >= 0)
                  {
                      SYSCALL(close(local->fd));
                  }
                  local->fd = -1;
                  return !Success;
              }
              else
              {
                  /* Report the file descriptor to all devices */
                  for (loop=0; loop < common->numDevices; ++loop)
                  {
                      common->deviceArray[loop]->fd = local->fd;
                  }
              }
              common->initNumber++;
          }
          device->initNumber = common->initNumber;
      }
  
      /*
       * Check our active area parameters. We support the following
       * three sets of mutually exclusive parameter sets:
       * 1) xMax/yMax. The origin (0,0) of the active area is the origin
       *    of the physical tablet. You therefore are describing the
       *    width and height of that active area.
       * 2) xOffset/xSize,yOffset/ySize. The origin (0,0) of the active
       *    area is defined as (xOffset,yOffset) (which we'll report as
       *    (0,0)). The size of the active area in width and height are
       *    expressed in coordinates as xSize/ySize. That is to say,
       *    if xOffset=5; yOffset=5, and xSize=10; ySize=10, then we will 
       *    have an active area beginning at (5,5) and ending at (15,15).
       *    Physical coordinate (5,5) is reported as (0,0); (15,15) is 
       *    reported as (10,10). The rest of the tablet is inert, as far as
       *    drawing area goes,
       * 3) xTop/xBottom,yTop/yBottom. The difference between this and
       *    #2 above is that all four parameters are physical coordinates
       *    on the tablet. Using the example above, xTop=5; yTop=5, and
       *    xBottom=15; yBottom=15. It is inferred mathematically that
       *    the overall active area is 10x10 coordinates.
       *
       * NOTE: Mutually exclusive means just that: choose the set of
       * parameters you like, and use them throughout. If you user xSize,
       * yOffset and xBottom, we'll have NO idea what you want, and quite
       * frankly you'll have tempted Fate enough that Bad Things(tm) will
       * happen to you. Do not complain to us!
       */
      if (device->xMax != VALUE_NA ||
          device->yMax != VALUE_NA)
      {
          /* Deal with larger-than-tablet and NA values.
           */
          if (device->xMax >  common->xCapacity ||
              device->xMax == VALUE_NA)
          {
              device->xMax = common->xCapacity;
              xf86Msg(X_CONFIG, "xMax value invalid; adjusting to %d\n",
                      device->xMax);
          }
          if (device->yMax > common->yCapacity ||
              device->yMax == VALUE_NA)
          {
              device->yMax = common->yCapacity;
              xf86Msg(X_CONFIG,"yMax value invalid; adjusting to %d\n",
                      device->yMax);
          }
  
          /*
           * Internally we use xTop/yTop/xBottom/yBottom
           * for everything. It's the easiest for us to work
           * with, vis-a-vis filtering.
           */
  
          device->xTop = 0;
          device->yTop = 0;
          device->xBottom = device->xMax;
          device->yBottom = device->yMax;
      }
  
      /*
       * Deal with xOffset/yOffset;xSize/ySize parameters
       */
      if (device->xSize   != VALUE_NA ||
          device->ySize   != VALUE_NA ||
          device->xOffset != VALUE_NA ||
          device->yOffset != VALUE_NA)
      {
          int message = 0;
          /* Simple sanity tests: nothing larger than the
           * tablet; nothing negative, except for an NA value.
           */
          if (device->xOffset  != VALUE_NA &&
              (device->xOffset >  common->xCapacity ||
               device->xOffset <  0))
          {
              message = 1;
              device->xOffset = 0;
          }
          if (device->yOffset  != VALUE_NA &&
              (device->yOffset >  common->yCapacity ||
               device->yOffset <  0))
          {
              message = 1;
              device->yOffset = 0;
          }
          if (device->xSize  != VALUE_NA &&
              (device->xSize >  common->xCapacity ||
               device->xSize <  0))
          {
              message = 1;
              device->xSize = common->xCapacity;
          }
          if (device->ySize  != VALUE_NA &&
              (device->ySize >  common->yCapacity ||
               device->ySize <  0))
          {
              message = 1;
              device->ySize = common->yCapacity;
          }
  
          /*
           * If one parameter is set but not the other, we'll
           * guess at something reasonable for the missing one.
           */
          if (device->xOffset == VALUE_NA ||
              device->xSize   == VALUE_NA)
          {
              if (device->xOffset == VALUE_NA)
              {
                  message = 1;
                  device->xOffset = 0;
              }
              else
              {
                  message = 1;
                  device->xSize = common->xCapacity - device->xOffset;
              }
          }
          if (device->yOffset == VALUE_NA ||
              device->ySize   == VALUE_NA)
          {
              if (device->yOffset == VALUE_NA)
              {
                  message = 1;
                  device->yOffset = 0;
              }
              else
              {
                  message = 1;
                  device->ySize = common->yCapacity - device->yOffset;
              }
          }
  
          /*
           * Do not allow the active area to exceed the size of the
           * tablet. To do this, we have to consider both parameters.
           * Assumption: xOffset/yOffset is always correct; deliver less
           * of the tablet than they asked for, if they asked for too much.
           */
          if (device->xOffset + device->xSize > common->xCapacity)
          {
              message = 1;
              device->xSize = common->xCapacity - device->xOffset;
          }
          if (device->yOffset + device->ySize > common->yCapacity)
          {
              message = 1;
              device->ySize = common->yCapacity - device->yOffset;
          }
  
          /*
           * 'message' is used to indicate that we've changed some parameter
           * during our filtration process. It's conceivable that we may
           * have changed parameters several times, so we without commentary
           * to the very end.
           */
          if (message == 1)
          {
              xf86Msg(X_CONFIG,"xOffset/yOffset;xSize/ySize values wrong.\n");
              xf86Msg(X_CONFIG,"xOffset adjusted to %d\n", device->xOffset);
              xf86Msg(X_CONFIG,"yOffset adjusted to %d\n", device->yOffset);
              xf86Msg(X_CONFIG,"xSize adjusted to %d\n", device->xSize);
              xf86Msg(X_CONFIG,"ySize adjusted to %d\n", device->ySize);
          }
  
          /*
           * Internally we use xTop/yTop/xBottom/yBottom
           * for everything. It's the easiest for us to work
           * with, vis-a-vis filtering.
           */
          device->xTop    = device->xOffset;
          device->yTop    = device->yOffset;
          device->xBottom = device->xOffset + device->xSize;
          device->yBottom = device->yOffset + device->ySize;
      }
  
      /*
       * Third set of parameters. Because everything internally
       * is expressed as xTop/yTop, etc., I'll do tests on transformed
       * values from the other parameters as need. My last chance to do
       * so.
       */
      if (device->xTop == VALUE_NA ||
          device->xTop <  0 ||
          device->xTop >  common->xCapacity)
      {
          device->xTop = 0;
          xf86Msg(X_CONFIG,"xTop invalid; adjusted to %d\n", device->xTop);
      }
      if (device->yTop == VALUE_NA ||
          device->yTop <  0 ||
          device->yTop >  common->yCapacity)
      {
          device->yTop = 0;
          xf86Msg(X_CONFIG,"yTop invalid; adjusted to %d\n", device->yTop);
      }
      if (device->xBottom == VALUE_NA ||
          device->xBottom <  0 ||
          device->xBottom >  common->xCapacity)
      {
          device->xBottom = common->xCapacity;
          xf86Msg(X_CONFIG,"xBottom invalid; adjusted to %d\n", 
                  device->xBottom);
      }
      if (device->yBottom == VALUE_NA ||
          device->yBottom <  0 ||
          device->yBottom >  common->yCapacity)
      {
          device->yBottom = common->yCapacity;
          xf86Msg(X_CONFIG,"yBottom invalid; adjusted to %d\n", 
                      device->yBottom);
      }
  
      /*
       * Determine the X screen we're going to be using.
       * If NA, or larger than the number of screens we
       * have, or negative, we've going for screen 0, e.g.,
       * 'default' screen.
       */
      if ( device->screenNo >= screenInfo.numScreens ||
           device->screenNo == VALUE_NA ||
           device->screenNo < 0) 
      {
          device->screenNo = 0;
          xf86Msg(X_CONFIG,"ScreenNo invalid; adjusted to %d\n", 
                  device->screenNo);
      }
  
      /* Calculate the ratio according to KeepShape, TopX and TopY */
  
      if (device->flags & KEEP_SHAPE_FLAG)
      {
          int xDiff, yDiff;
  
          xDiff = common->xCapacity - device->xTop;
          yDiff = common->yCapacity - device->yTop;
  
          tabletRatio = (double) xDiff / (double) yDiff;
          screenRatio = (double) screenInfo.screens[device->screenNo]->width /
                        (double) screenInfo.screens[device->screenNo]->height;
  
          DBG(2, ErrorF("Screen %d: screenRatio = %.3g, tabletRatio = %.3g\n",
                    device->screenNo, screenRatio, tabletRatio));
  
          if (screenRatio > tabletRatio)
          {
              gap = (int)((double)common->yCapacity *
                         (1.0 - tabletRatio/screenRatio));
              device->xBottom = common->xCapacity;
              device->yBottom = common->yCapacity - gap;
              DBG(2, ErrorF("Screen %d: 'Y' Gap of %d computed\n",
                    device->screenNo, gap));
          }
          else
          {
              gap = (int)((double)common->xCapacity *
                         (1.0 - screenRatio/tabletRatio));
              device->xBottom = common->xCapacity - gap;
              device->yBottom = common->yCapacity;
              DBG(2, ErrorF("Screen %d: 'X' Gap of %d computed\n",
                    device->screenNo, gap));
          }
      }
  
      xFactor = (double)screenInfo.screens[device->screenNo]->width/
          (double)(device->xBottom - device->xTop);
      yFactor = (double)screenInfo.screens[device->screenNo]->height/
          (double)(device->yBottom - device->yTop);
  
      /* 
       * Check threshold correctness
       */
      if (device->xThreshold > common->xCapacity ||
          device->xThreshold == VALUE_NA ||
          device->xThreshold <  0)
      {
          device->xThreshold = 0;
      }
  
      if (device->yThreshold > common->yCapacity ||
          device->yThreshold == VALUE_NA ||
          device->yThreshold < 0)
      {
          device->yThreshold = 0;
      }
  
      if (device->zThreshold > common->zCapacity ||
          device->zThreshold == VALUE_NA ||
          device->zThreshold < 0) 
      {
          device->zThreshold = 0;
      }
  
      /* Create axisStructs for every axis we support.
       * NOTE: min_resolution and max_resolution infers to
       * me a programmability to increase/decrease resolution.
       * We don't support that, so min & max = current_resolution.
       */
      InitValuatorAxisStruct(pDriver,                 /* X resolution */
                 0,                                   /* axis_id */
                 0,                                   /* min value */
                 device->xBottom - device->xTop,      /* max value */
                 LPI2CPM(375),                        /* resolution */
                 LPI2CPM(375),                        /* min_resolution */
                 LPI2CPM(375));                       /* max_resolution */
  
      InitValuatorAxisStruct(pDriver,                 /* Y Resolution */
                 1,                                   /* axis_id */
                 0,                                   /* min value */
                 device->yBottom - device->yTop,      /* max value */
                 LPI2CPM(375),                        /* resolution */
                 LPI2CPM(375),                        /* min_resolution */
                 LPI2CPM(375));                       /* max_resolution */
  
      InitValuatorAxisStruct(pDriver,                 /* Pressure */
                 2,                                   /* axis_id */
                 0,                                   /* min value */
                 511,                                 /* max value */
                 512,                                 /* resolution */
                 512,                                 /* min_resolution */
                 512);                                /* max_resolution */
  
      InitValuatorAxisStruct(pDriver,                 /* xTilt */
                 3,                                   /* axis id */
                 -128,                                /* min value */
                 127,                                 /* max value */
                 256,                                 /* resolution */
                 256,                                 /* min_resolution */
                 256);                                /* max_resolution */
  
      InitValuatorAxisStruct(pDriver,                 /* yTilt */
                 4,                                   /* axis_id */
                 -128,                                /* min value */
                 127,                                 /* max value */
                 256,                                 /* resolution */
                 256,                                 /* min_resolution */
                 256);                                /* max_resolution */
  
      /*
       * The sixth axis would be for wheel support. We do not have
       * any wheel devices. But if we did, it would be allocated
       * here.
       */
      return (local->fd != -1);
  }
  
  /*
   * xf86AiptekProc
   *
   *  Call dispatcher for this driver.
   */
  static int
  xf86AiptekProc(DeviceIntPtr pAiptek, int requestCode)
  {
      CARD8           map[512+1];
      int             numAxes;
      int             numButtons;
      int             loop;
      LocalDevicePtr  local  = (LocalDevicePtr)pAiptek->public.devicePrivate;
      AiptekDevicePtr device = (AiptekDevicePtr)PRIVATE(pAiptek);
  
      DBG(2, ErrorF("xf86AiptekProc() type=%s flags=%d request=%d\n",
                (DEVICE_ID(device->flags) == STYLUS_ID) ? "stylus" :
                (DEVICE_ID(device->flags) == CURSOR_ID) ? "cursor" : "eraser",
                device->flags, requestCode));
  
      switch (requestCode)
      {
          case DEVICE_INIT:
          {
              DBG(1, ErrorF("xf86AiptekProc request=INIT\n"));
              numAxes    = 5;            /* X, Y, Z, xTilt, yTilt */
              numButtons = 5;
  
              for(loop=1; loop<=numButtons; ++loop)
              {
                  map[loop] = loop;
              }
  
              if (InitButtonClassDeviceStruct(pAiptek,numButtons,map) == FALSE)
              {
                  ErrorF("Unable to init Button Class Device\n");
                  return !Success;
              }
  
              if (InitFocusClassDeviceStruct(pAiptek) == FALSE) 
              {
                  ErrorF("Unable to init Focus Class Device\n");
                  return !Success;
              }
  
              if (InitPtrFeedbackClassDeviceStruct(pAiptek,
                          xf86AiptekControlProc) == FALSE)
              {
                  ErrorF("Unable to init Pointer Feedback Class Device\n");
                  return !Success;
              }
  
              if (InitProximityClassDeviceStruct(pAiptek) == FALSE)
              {
                  ErrorF("Unable to init Proximity Class Device\n");
                  return !Success;
              }
  
              if (InitKeyClassDeviceStruct(pAiptek, &keysyms, NULL) ==FALSE)
              {
                  ErrorF("Unable to init Key Class Device\n");
                  return !Success;
              }
  
              if (InitValuatorClassDeviceStruct(pAiptek,
                     numAxes,
                     xf86GetMotionEvents,
                     local->history_size,
                     ((device->flags & ABSOLUTE_FLAG) 
                          ? Absolute : Relative) | OutOfProximity ) == FALSE)
              {
                  ErrorF("Unable to allocate Valuator Class Device\n");
                  return !Success;
              }
  
              /* Allocate the motion history buffer if needed */
              xf86MotionHistoryAllocate(local);
  
              /* Open the device to gather information */
              xf86AiptekOpenDevice(pAiptek);
          }
          break;
  
          case DEVICE_ON:
          {
              DBG(1, ErrorF("xf86AiptekProc request=ON\n"));
  
              if ((local->fd < 0) && 
                  (!xf86AiptekOpenDevice(pAiptek)))
              {
                  ErrorF("Unable to open aiptek device\n");
                  return !Success;
              }
              ErrorF("Able to open aiptek device\n");
              xf86AddEnabledDevice(local);
              pAiptek->public.on = TRUE;
          }
          break;
  
          case DEVICE_OFF:
          {
              DBG(1, ErrorF("xf86AiptekProc request=%s\n", 
                              (requestCode == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
              if (local->fd >= 0)
              {
                  xf86AiptekClose(local);
                  xf86RemoveEnabledDevice(local);
              }
              pAiptek->public.on = FALSE;
          }
          break;
  
          case DEVICE_CLOSE:
          {
              DBG(1, ErrorF("xf86AiptekProc request=%s\n", 
                             (requestCode == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
              xf86AiptekClose(local);
          }
          break;
  
          default:
          {
              ErrorF("xf86AiptekProc - Unsupported mode=%d\n", requestCode);
              return !Success;
          }
          break;
      }
      DBG(2, ErrorF("xf86AiptekProc Success request=%d\n", requestCode ));
      return Success;
  }
  
  /*
   * xf86AiptekClose
   * Perhaps this will close the device
   */
  static void
  xf86AiptekClose(LocalDevicePtr local)
  {
      if (local->fd >= 0)
      {
          SYSCALL(close(local->fd));
      }
      local->fd = -1;
  }
  
  /*
   * xf86AiptekChangeControl
   * Allow the user to change the tablet resolution -- we have an issue
   * insofar as we don't know how to write to the tablet. And furthermore,
   * even if we DID know how to write to the tablet, it doesn't support
   * a "change resolution" call. We tried to avoid this by claiming when
   * creating axisStructs that minRes = curRes = maxRes. So, we should never
   * get dispatched.
   */
  static int
  xf86AiptekChangeControl(LocalDevicePtr local, xDeviceCtl *control)
  {
      xDeviceResolutionCtl    *res;
      int                     *resolutions;
  
      DBG(3, ErrorF("xf86AiptekChangeControl() entered\n"));
  
      res = (xDeviceResolutionCtl *)control;
      
      if ((control->control != DEVICE_RESOLUTION) ||
          (res->num_valuators < 1))
      {
          DBG(3, ErrorF("xf86AiptekChangeControl abends\n"));
          return (BadMatch);
      }
  
      resolutions = (int *)(res +1);
      
      DBG(3, ErrorF("xf86AiptekChangeControl changing to res %d\n",
                resolutions[0]));
  
      /* We don't know how to write, yet
       *
       * sprintf(str, "SU%d\r", resolutions[0]);
       * SYSCALL(write(local->fd, str, strlen(str)));
       */
  
      return(Success);
  }
  
  /*
   * xf86AiptekSwitchMode
   * Switches the mode. For now just absolute or relative, hopefully
   * more on the way.
   */
  static int
  xf86AiptekSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
  {
      LocalDevicePtr  local  = (LocalDevicePtr)dev->public.devicePrivate;
      AiptekDevicePtr device = (AiptekDevicePtr)(local->private);
  
      DBG(3, ErrorF("xf86AiptekSwitchMode() dev=%p mode=%d\n", dev, mode));
  
      switch(mode)
      {
          case Absolute:
          {
              device->flags |= ABSOLUTE_FLAG;
          }
          break;
  
          case Relative:
          {
              device->flags &= ~ABSOLUTE_FLAG;
          }
          break;
  
          default:
          {
              DBG(1, ErrorF("xf86AiptekSwitchMode dev=%p invalid mode=%d\n",
                     dev, mode));
              return BadMatch;
          }
          break;
      }
  
      return Success;
  }
  
  /*
   * xf86AiptekAllocate
   * Allocates the device structures for the Aiptek.
   */
  static LocalDevicePtr
  xf86AiptekAllocate(char* name,
                  int   flag)
  {
      LocalDevicePtr    local;
      LocalDevicePtr*   deviceArray;
      AiptekDevicePtr   device;
      AiptekCommonPtr   common;
  
      DBG(3, ErrorF("xf86AiptekAllocate, with %s and %d\n", name, flag));
  
      device = (AiptekDevicePtr) xalloc(sizeof(AiptekDeviceRec));
      if (!device)
      {
          DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'device'\n"));
          return NULL;
      }
  
      common = (AiptekCommonPtr) xalloc(sizeof(AiptekCommonRec));
      if (!common)
      {
          DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'common'\n"));
          xfree(device);
          return NULL;
      }
  
      deviceArray = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr));
      if (!deviceArray)
      {
          DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'deviceArray'\n"));
          xfree(device);
          xfree(common);
          return NULL;
      }
  
  
      local = xf86AllocateInput(aiptekDrv, 0);
      if (!local)
      {
          DBG(3, ErrorF("xf86AiptekAllocate failed at xf86AllocateInput()\n"));
          xfree(device);
          xfree(common);
          xfree(deviceArray);
          return NULL;
      }
  
      local->name =                       name;
      local->type_name =                  "Aiptek";
      local->flags =                      0;
      local->device_control =             xf86AiptekProc;
      local->read_input =                 xf86AiptekHIDReadInput;
      local->control_proc =               xf86AiptekChangeControl;
      local->close_proc =                 xf86AiptekClose;
      local->switch_mode =                xf86AiptekSwitchMode;
      local->conversion_proc =            xf86AiptekConvert;
      local->reverse_conversion_proc =    xf86AiptekReverseConvert;
  
      local->fd =             VALUE_NA;
      local->atom =           0;
      local->dev =            NULL;
      local->private =        device;
      local->private_flags =  0;
      local->history_size  =  0;
  
      device->flags =         flag;       /* various flags (device type, 
                                           * coordinate type */
      device->xSize =         VALUE_NA; /* Active Area X */
      device->ySize =         VALUE_NA; /* Active Area Y */
      device->xOffset =       VALUE_NA; /* Active area offset X */
      device->yOffset =       VALUE_NA; /* Active area offset Y */
      device->xMax =          VALUE_NA; /* Max allowed X value */
      device->yMax =          VALUE_NA; /* Max allowed Y value */
      device->zMin =          VALUE_NA; /* Min allowed Z value */
      device->zMax =          VALUE_NA; /* Max allowed Z value */
      device->xTop =          VALUE_NA; /* Upper Left X coordinate */
      device->yTop =          VALUE_NA; /* Upper Left Y coordinate */
      device->xBottom =       VALUE_NA; /* Lower Right X coordinate */
      device->yBottom =       VALUE_NA; /* Lower Right Y coordinate */
      device->xThreshold =    VALUE_NA; /* X delta must be greater than */
      device->yThreshold =    VALUE_NA; /* Y delta must be greater than */
      device->zThreshold =    VALUE_NA; /* Z delta must be greater than */
      device->xTiltThreshold =VALUE_NA;
      device->yTiltThreshold =VALUE_NA;
      device->zMode =         VALUE_NA; /* Z: linear, soft, hard log */
  
      device->initNumber =    VALUE_NA; /* avoid re-init devices */
      device->screenNo =      VALUE_NA; /* Attached to X screen */
  
      device->common =        common;   /* Common info pointer */
  
      /* Record of the event currently being read of the queue */
      common->currentValues.eventType = 0;   /* Device event is for, e.g., */
                                              /* STYLUS, RUBBER, CURSOR */
      common->currentValues.x         = 0;   /* X coordinate */
      common->currentValues.y         = 0;   /* Y coordinate */
      common->currentValues.z         = 0;   /* Z (pressure) */
      common->currentValues.xTilt     = 0;   /* XTilt */
      common->currentValues.yTilt     = 0;   /* YTilt */
      common->currentValues.proximity = 0;   /* proximity bit */
      common->currentValues.macroKey  = VALUE_NA;   /* tablet macro key code */
      common->currentValues.button    = 0;   /* bitmask of buttons pressed */
      common->currentValues.distance  = 0;   /* currently unsupported */
      common->currentValues.wheel     = 0;   /* likewise */
  
      /* Record of the event previously read off of the queue */
      common->previousValues.eventType = 0;  /* Same comments as above */
      common->previousValues.x         = 0;
      common->previousValues.y         = 0;
      common->previousValues.z         = 0;
      common->previousValues.xTilt     = 0;
      common->previousValues.yTilt     = 0;
      common->previousValues.proximity = 0;
      common->previousValues.macroKey  = VALUE_NA;
      common->previousValues.button    = 0;
      common->previousValues.distance  = 0;
      common->previousValues.wheel     = 0;
  
      common->deviceName =    "";             /* device file name */
      common->flags =         0;              /* various flags */
      common->deviceArray =   deviceArray;    /* Array of tablets */
      common->deviceArray[0]= local;          /* with local as element */
      common->numDevices =    1;              /* number of devices */
  
      common->xCapacity =     0;              /* tablet's max X value */
      common->yCapacity =     0;              /* tablet's max Y value */
      common->zCapacity =     0;              /* tablet's max Z value */
      common->open =          xf86AiptekOpen; /* Open function */
  
      return local;
  }
  
  /*
   * xf86AiptekAllocateStylus
   */
  static LocalDevicePtr
  xf86AiptekAllocateStylus(void)
  {
      LocalDevicePtr local = xf86AiptekAllocate(XI_STYLUS, STYLUS_ID);
      
      if (local)
      {
          local->type_name = "Stylus";
      }
      return local;
  }
  
  /*
   * xf86AiptekAllocateCursor
   */
  static LocalDevicePtr
  xf86AiptekAllocateCursor(void)
  {
      LocalDevicePtr local = xf86AiptekAllocate(XI_CURSOR, CURSOR_ID);
      
      if (local)
      {
          local->type_name = "Cursor";
      }
      return local;
  }
  
  /*
   * xf86AiptekAllocateEraser
   */
  static LocalDevicePtr
  xf86AiptekAllocateEraser(void)
  {
      LocalDevicePtr local = xf86AiptekAllocate(XI_ERASER,
              ABSOLUTE_FLAG|ERASER_ID);
      
      if (local)
      {
          local->type_name = "Eraser";
      }
      return local;
  }
  
  /*
   * Stylus device association
   */
  DeviceAssocRec aiptek_stylus_assoc =
  {
      STYLUS_SECTION_NAME,        /* config_section_name */
      xf86AiptekAllocateStylus    /* device_allocate */
  };
  
  /*
   * Cursor device association
   */
  DeviceAssocRec aiptek_cursor_assoc =
  {
      CURSOR_SECTION_NAME,        /* config_section_name */
      xf86AiptekAllocateCursor    /* device_allocate */
  };
  
  /*
   * Eraser device association
   */
  DeviceAssocRec aiptek_eraser_assoc =
  {
      ERASER_SECTION_NAME,        /* config_section_name */
      xf86AiptekAllocateEraser    /* device_allocate */
  };
  
  /*
   * xf86AiptekUninit --
   *
   * called when the driver is unloaded.
   */
  static void
  xf86AiptekUninit(InputDriverPtr    drv,
                   LocalDevicePtr    local,
                   int               flags)
  {
      AiptekDevicePtr device = (AiptekDevicePtr) local->private;
  
      DBG(1, ErrorF("xf86AiptekUninit\n"));
  
      xf86AiptekProc(local->dev, DEVICE_OFF);
  
      if (device->common && device->common->xCapacity != -10101)
      {
          device->common->xCapacity = -10101;
          xfree(device->common);
      }
      xfree (device);
      xf86DeleteInput(local, 0);
  }
  
  /*
   * xf86AiptekInit --
   *
   * Called when the module subsection is found in XF86Config
   */
  static InputInfoPtr
  xf86AiptekInit(InputDriverPtr    drv,
                 IDevPtr           dev,
                 int               flags)
  {
      LocalDevicePtr    local     = NULL;
      LocalDevicePtr    fakeLocal = NULL;
      AiptekDevicePtr   device    = NULL;
      AiptekCommonPtr   common    = NULL;
      LocalDevicePtr    locals;
      char*             s;
      int               shared;
  
      aiptekDrv = drv;
  
      xf86Msg(X_INFO, "xf86AiptekInit(): begins\n");
  
      fakeLocal = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec));
      if (!fakeLocal)
      {
          return NULL;
      }
  
      fakeLocal->conf_idev = dev;
  
      /*
       * fakeLocal is here so it can have default serial init values.
       * Is this something to remove? TODO
       */
      xf86CollectInputOptions(fakeLocal, default_options, NULL);
  
  /* Type */
      s = xf86FindOptionValue(fakeLocal->options, "Type");
      if (s && (xf86NameCmp(s, "stylus") == 0))
      {
          local = xf86AiptekAllocateStylus();
      }
      else if (s && (xf86NameCmp(s, "cursor") == 0))
      {
          local = xf86AiptekAllocateCursor();
      }
      else if (s && (xf86NameCmp(s, "eraser") == 0))
      {
          local = xf86AiptekAllocateEraser();
      }
      else
      {
          xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
                    "Must be one of 'stylus', 'cursor', or 'eraser'\n",
                    dev->identifier);
      }
  
      if(!local)
      {
          xfree(fakeLocal);
          return NULL;
      }
  
      device = (AiptekDevicePtr) local->private;
  
      common              = device->common;
  
      local->options      = fakeLocal->options;
      local->conf_idev    = fakeLocal->conf_idev;
      local->name         = dev->identifier;
      xfree(fakeLocal);
  
  /* Device */
  /* (mandatory) */
      common->deviceName = xf86FindOptionValue(local->options, "Device");
      if(!common->deviceName)
      {
          xf86Msg(X_ERROR, "%s: No Device specified.\n", dev->identifier);
          goto SetupProc_fail;
      }
  
      /*
       * Lookup to see if there is another aiptek device sharing the
       * same device.
       */
  
      shared = 0;
      for (locals = xf86FirstLocalDevice();
           locals != NULL;
           locals = locals->next)
      {
          if((local != locals) &&
             (locals->device_control == xf86AiptekProc) &&
             (strcmp(((AiptekDevicePtr)locals->private)->common->deviceName,
                      common->deviceName) == 0))
          {
              xf86Msg(X_CONFIG,
                      "xf86AiptekConfig: device shared between %s and %s\n",
                      local->name,
                      locals->name);
  
              shared = 1;
  
              xfree(common->deviceArray);
              xfree(common);
  
              common = device->common =
                  ((AiptekDevicePtr) locals->private)->common;
              common->numDevices++;
              common->deviceArray = (LocalDevicePtr*)xrealloc(common->deviceArray,
                      sizeof(LocalDevicePtr)*common->numDevices);
              common->deviceArray[common->numDevices-1] = local;
              break;
          }
          else
          {
              xf86Msg(X_CONFIG, 
                      "xf86AiptekConfig: device not shared btw %s and %s\n",
                      local->name, locals->name);
          }
      }
  
      /* Process the common options */
      xf86ProcessCommonOptions(local, local->options);
  
      /* If this is the first device using the aiptek driver, let's open
       * the interface so as to obtain legit values for xCapacity and yCapacity
       * (and then quickly close it). Yes, the word for this is "kludge".
       * I am sufficiently ashamed :-) TODO kludge alert!
       */
      if ( shared == 0)
      {
          xf86AiptekHIDOpen(local);
          close(local->fd);
          local->fd=-1;
      }
  
      /* Optional configuration */
      xf86Msg(X_CONFIG, "%s device is %s\n", dev->identifier,
              common->deviceName);
  
  /* DebugLevel */
      debug_level = xf86SetIntOption(local->options, "DebugLevel", debug_level);
      if ( debug_level > 0)
      {
          xf86Msg(X_CONFIG, "Debug level set to %d\n", debug_level);
      }
  
  /* zMode */
      s = xf86FindOptionValue(local->options, "Pressure");
      if ( s && (xf86NameCmp(s, "hard") == 0))
      {
          device->zMode = PRESSURE_MODE_HARD_SMOOTH;
      }
      else if ( s && (xf86NameCmp(s, "soft") == 0))
      {
          device->zMode = PRESSURE_MODE_SOFT_SMOOTH;
      }
      else if (s && (xf86NameCmp(s, "normal") == 0))
      {
          device->zMode = PRESSURE_MODE_LINEAR;
      }
      else if (s)
      {
          xf86Msg(X_ERROR, "%s: invalid Mode ('normal', 'soft' or 'hard').\n",
              dev->identifier);
      }
  
  /* Mode */
      s = xf86FindOptionValue(local->options, "Mode");
      if (s && (xf86NameCmp(s, "absolute") == 0))
      {
          device->flags |= ABSOLUTE_FLAG;
      }
      else if (s && (xf86NameCmp(s, "relative") == 0)) 
      {
          device->flags &= ~ABSOLUTE_FLAG;
      }
      else if (s)
      {
          xf86Msg(X_ERROR, "%s: invalid Mode ('absolute' or 'relative').\n",
              dev->identifier);
          device->flags |= ABSOLUTE_FLAG;
      }
      xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name,
          (device->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");
  
  #ifdef LINUX_INPUT
      /* The define-name is accurate; the XFree86 keyword is not. We are
       * reading from a Linux kernel "Input" device. The Input device
       * layer generally supports mice, joysticks, and keyboards. As
       * an extension, the Input device layer also supports HID devices.
       * HID is a standard specified by the USB Implementors Forum. Ergo,
       * 99.9% of HID devices are USB devices.
       *
       * This option is misnamed, misunderstood, misanthrope. Maybe.
       */
      if (xf86SetBoolOption(local->options, "USB",
                            (common->open == xf86AiptekHIDOpen)))
      {
          local->read_input=xf86AiptekHIDReadInput;
          common->open=xf86AiptekHIDOpen;
          xf86Msg(X_CONFIG, "%s: reading USB link\n", dev->identifier);
      }
  #else
      if (xf86SetBoolOption(local->options, "USB", 0))
      {
          ErrorF("The Aiptek USB driver isn't available for your platform.\n");
          goto SetupProc_fail;
      }
  #endif
  
  /* ScreenNo */
      device->screenNo = xf86SetIntOption(local->options, "ScreenNo", VALUE_NA);
      if (device->screenNo != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: attached to screen number %d\n",
                  dev->identifier, device->screenNo);
      }
  
  /* KeepShape */
      if (xf86SetBoolOption(local->options, "KeepShape", 0))
      {
          device->flags |= KEEP_SHAPE_FLAG;
          xf86Msg(X_CONFIG, "%s: keeps shape\n", dev->identifier);
      }
  
  /* XSize */
      device->xSize = xf86SetIntOption(local->options, "XSize", device->xSize);
      device->xSize = xf86SetIntOption(local->options, "SizeX", device->xSize);
      if (device->xSize != VALUE_NA) 
      {
          xf86Msg(X_CONFIG, "%s: XSize/SizeX = %d\n", dev->identifier,
                  device->xSize);
      }
  
  /* YSize */
      device->ySize = xf86SetIntOption(local->options, "YSize", device->ySize);
      device->ySize = xf86SetIntOption(local->options, "SizeY", device->ySize);
      if (device->ySize != VALUE_NA) 
      {
          xf86Msg(X_CONFIG, "%s: YSize/SizeY = %d\n", dev->identifier,
                  device->ySize);
      }
  
  /* XOffset */
      device->xOffset = xf86SetIntOption(local->options, "XOffset", 
              device->xOffset);
      device->xOffset = xf86SetIntOption(local->options, "OffsetX", 
              device->xOffset);
      if (device->xOffset != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: XOffset/OffsetX = %d\n", dev->identifier,
                  device->xOffset);
      }
  
  /* YOffset */
      device->yOffset = xf86SetIntOption(local->options, "YOffset",
              device->yOffset);
      device->yOffset = xf86SetIntOption(local->options, "OffsetY",
              device->yOffset);
      if (device->yOffset != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: YOffset/OffsetY = %d\n", dev->identifier,
                  device->yOffset);
      }
  
  /* XThreshold */
      device->xThreshold = xf86SetIntOption(local->options, "XThreshold",
              device->xThreshold);
      device->xThreshold = xf86SetIntOption(local->options, "ThresholdX",
              device->xThreshold);
      if (device->xThreshold != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: XThreshold/ThresholdX = %d\n",
                  dev->identifier, device->xThreshold);
      }
  
  /* YThreshold */
      device->yThreshold = xf86SetIntOption(local->options, "YThreshold",
              device->yThreshold);
      device->yThreshold = xf86SetIntOption(local->options, "ThresholdY",
              device->yThreshold);
      if (device->yThreshold != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: YThreshold/ThresholdY = %d\n",
                  dev->identifier, device->yThreshold);
      }
  
  /* ZThreshold */
      device->zThreshold = xf86SetIntOption(local->options, "ZThreshold",
              device->zThreshold);
      device->zThreshold = xf86SetIntOption(local->options, "ThresholdZ",
              device->zThreshold);
      if (device->zThreshold != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: ZThreshold/ThresholdZ = %d\n",
                  dev->identifier, device->zThreshold);
      }
  
  /* XTiltThreshold */
      device->xTiltThreshold = xf86SetIntOption(local->options, "XTiltThreshold",
              device->xTiltThreshold);
      device->xTiltThreshold = xf86SetIntOption(local->options, "ThresholdXTilt",
              device->xTiltThreshold);
      if (device->xTiltThreshold != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: XTiltThreshold = %d\n",
                  dev->identifier, device->xTiltThreshold);
      }
  
  /* YTiltThreshold */
      device->yTiltThreshold = xf86SetIntOption(local->options, "YTiltThreshold",
              device->yTiltThreshold);
      device->yTiltThreshold = xf86SetIntOption(local->options, "ThresholdYTilt",
              device->yTiltThreshold);
      if (device->yTiltThreshold != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: YTiltThreshold = %d\n",
                  dev->identifier, device->yTiltThreshold);
      }
  
  /* XMax */
      device->xMax = xf86SetIntOption(local->options, "XMax", device->xMax);
      device->xMax = xf86SetIntOption(local->options, "MaxX", device->xMax);
      if (device->xMax != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: XMax/MaxX = %d\n", dev->identifier,
                  device->xMax);
      }
  
  /* YMax */
      device->yMax = xf86SetIntOption(local->options, "YMax", device->yMax);
      device->yMax = xf86SetIntOption(local->options, "MaxY", device->yMax);
      if (device->yMax != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: YMax/MaxY = %d\n", dev->identifier,
                  device->yMax);
      }
  
  /* ZMax */
      device->zMax = xf86SetIntOption(local->options, "ZMax", device->zMax);
      device->zMax = xf86SetIntOption(local->options, "MaxZ", device->zMax);
      if (device->zMax != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: ZMax/MaxZ = %d\n", dev->identifier,
                  device->zMax);
      }
  
  /* ZMin */
      device->zMin = xf86SetIntOption(local->options, "ZMin", device->zMin);
      device->zMin = xf86SetIntOption(local->options, "MinZ", device->zMin);
      if (device->zMin != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: ZMin/MinZ = %d\n", dev->identifier,
                  device->zMin);
      }
  
  /* TopX */
      device->xTop = xf86SetIntOption(local->options, "TopX", device->xTop);
      device->xTop = xf86SetIntOption(local->options, "XTop", device->xTop);
      if (device->xTop != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: TopX/XTop = %d\n", dev->identifier,
                  device->xTop);
      }
  
  /* TopY */
      device->yTop = xf86SetIntOption(local->options, "TopY", device->yTop);
      device->yTop = xf86SetIntOption(local->options, "YTop", device->yTop);
      if (device->yTop != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: TopY/YTop = %d\n", dev->identifier,
                  device->yTop);
      }
  
  /* BottomX */
      device->xBottom = xf86SetIntOption(local->options, "BottomX",
              device->xBottom);
      device->xBottom = xf86SetIntOption(local->options, "XBottom",
              device->xBottom);
      if (device->xBottom != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: BottomX/XBottom = %d\n", dev->identifier,
                  device->xBottom);
      }
  
  /* BottomY */
      device->yBottom = xf86SetIntOption(local->options, "BottomY",
              device->yBottom);
      device->yBottom = xf86SetIntOption(local->options, "YBottom",
              device->yBottom);
      if (device->yBottom != VALUE_NA)
      {
          xf86Msg(X_CONFIG, "%s: BottomY/YBottom = %d\n", dev->identifier,
                  device->yBottom);
      }
  
  /* InvX */
      if (xf86SetBoolOption(local->options, "InvX", FALSE))
      {
          device->flags |= INVX_FLAG;
          xf86Msg(X_CONFIG, "%s: InvX\n", dev->identifier);
      }
  
  /* InvY */
      if (xf86SetBoolOption(local->options, "InvY", FALSE))
      {
          device->flags |= INVY_FLAG;
          xf86Msg(X_CONFIG, "%s: InvY\n", dev->identifier);
      }
  
  /* BaudRate */
      {
          int val;
          val = xf86SetIntOption(local->options, "BaudRate", 0);
  
          switch(val)
          {
              case 19200:
                  break;
              case 9600:
                  break;
              default:
                  xf86Msg(X_ERROR, "%s: Illegal BaudRate (9600 or 19200).",
                          dev->identifier);
                  break;
          }
          if (xf86Verbose)
          {
              xf86Msg(X_CONFIG, "%s: BaudRate %u\n", dev->identifier,
                      val);
          }
      }
      xf86Msg(X_CONFIG, "%s: xf86AiptekInit() finished\n", dev->identifier);
  
      /* Mark the device as configured */
      local->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
  
      /* return the LocalDevice */
      return (local);
  
  SetupProc_fail:
      if (common)
          xfree(common);
      if (device)
          xfree(device);
      if (local)
          xfree(local);
      return NULL;
  }
  
  /*
   ***************************************************************************
   *
   * Dynamic loading functions
   *
   ***************************************************************************
   */
  #ifdef XFree86LOADER
  /*
   * xf86AiptekUnplug --
   *
   * called when the module subsection is found in XF86Config
   */
  static void
  xf86AiptekUnplug(pointer p)
  {
      DBG(1, ErrorF("xf86AiptekUnplug\n"));
  }
  
  /*
   * xf86AiptekPlug --
   *
   * called when the module subsection is found in XF86Config
   */
  static pointer
  xf86AiptekPlug(pointer    module,
                 pointer    options,
                 int*       errmaj,
                 int*       errmin)
  {
      DBG(1, ErrorF("xf86AiptekPlug\n"));
  
      xf86AddInputDriver(&AIPTEK, module, 0);
  
      return module;
  }
  
  static XF86ModuleVersionInfo xf86AiptekVersionRec =
  {
      "aiptek",
      MODULEVENDORSTRING,
      MODINFOSTRING1,
      MODINFOSTRING2,
      XORG_VERSION_CURRENT,
      1, 0, 0,
      ABI_CLASS_XINPUT,
      ABI_XINPUT_VERSION,
      MOD_CLASS_XINPUT,
      {0, 0, 0, 0}        /* signature, to be patched into the file by
                           * a tool
                           * */
  };
  
  _X_EXPORT XF86ModuleData aiptekModuleData =
  {
      &xf86AiptekVersionRec,
      xf86AiptekPlug,
      xf86AiptekUnplug
  };
  
  #endif /* XFree86LOADER */
  
  /* end of xf86Aiptek.c */
  