Logo Search packages:      
Sourcecode: xfree86 version File versions

xf86Wacom.c

/* $XConsortium: xf86Wacom.c /main/20 1996/10/27 11:05:20 kaleb $ */
/*
 * Copyright 1995-2001 by Frederic Lepied, France. <Lepied@XFree86.org>
 *                                                                            
 * 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  Frederic   Lepied not  be  used  in
 * advertising or publicity pertaining to distribution of the software without
 * specific,  written      prior  permission.     Frederic  Lepied   makes  no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.                   
 *                                                                            
 * FREDERIC  LEPIED DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
 * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
 * EVENT  SHALL FREDERIC  LEPIED 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.
 *
 */

/* $XFree86: xc/programs/Xserver/hw/xfree86/input/wacom/xf86Wacom.c,v 1.32 2003/01/15 03:48:36 dawes Exp $ */

/*
 * This driver is only able to handle the Wacom IV and Wacom V protocols.
 *
 * Wacom V protocol work done by Raph Levien <raph@gtk.org> and
 * Frédéric Lepied <lepied@xfree86.org>.
 *
 * Many thanks to Dave Fleck from Wacom for the help provided to
 * build this driver.
 *
 * Modified for Linux USB by MATSUMURA Namihiko,
 * Daniel Egger, Germany. <egger@suse.de>,
 * Frederic Lepied <lepied@xfree86.org>,
 * Brion Vibber <brion@pobox.com>,
 * Aaron Optimizer Digulla <digulla@hepe.com>,
 * Jonathan Layes <jonathan@layes.com>.
 *
 */

static const char identification[] = "$Identification: 23 $";

#include "xf86Version.h"

#if XF86_VERSION_CURRENT >= XF86_VERSION_NUMERIC(3,9,0,0,0)
#define XFREE86_V4 1
#endif

#ifdef LINUX_INPUT
#include <asm/types.h>
#include <linux/input.h>

/* max number of input events to read in one read call */
#define MAX_EVENTS 50

/* keithp - a hack to avoid redefinitions of these in xf86str.h */
#ifdef BUS_PCI
#undef BUS_PCI
#endif
#ifdef BUS_ISA
#undef BUS_ISA
#endif
#endif

/* post 3.9 headers */

#ifndef XFree86LOADER
#include <unistd.h>
#include <errno.h>
#endif

#include "misc.h"
#include "xf86.h"
#define NEED_XF86_TYPES
#if !defined(DGUX)
#include "xf86_ansic.h"
#include "xisb.h"
#endif
#include "xf86_OSproc.h"
#include "xf86Xinput.h"
#include "exevents.h"         /* Needed for InitValuator/Proximity stuff */
#include "keysym.h"
#include "mipointer.h"

#ifdef XFree86LOADER
#include "xf86Module.h"
#endif

#define wait_for_fd(fd) xf86WaitForInput((fd), 1000000)
#define tcflush(fd, n) xf86FlushInput((fd))
#undef read
#define read(a,b,c) xf86ReadSerial((a),(b),(c))
#undef write
#define write(a,b,c) xf86WriteSerial((a),(char*)(b),(c))
#undef close
#define close(a) xf86CloseSerial((a))
#define XCONFIG_PROBED "(==)"
#define XCONFIG_GIVEN "(**)"
#define xf86Verbose 1
#undef PRIVATE
#define PRIVATE(x) XI_PRIVATE(x)

/* 
 * Be sure to set vmin appropriately for your device's protocol. You want to
 * read a full packet before returning
 */
static const char *default_options[] =
{
      "BaudRate", "9600",
      "StopBits", "1",
      "DataBits", "8",
      "Parity", "None",
      "Vmin", "1",
      "Vtime", "10",
      "FlowControl", "Xoff",
      NULL
};

static InputDriverPtr wcmDrv;


#if defined(__QNX__) || defined(__QNXNTO__)
#define POSIX_TTY
#endif

/******************************************************************************
 * debugging macro
 *****************************************************************************/
#ifdef DBG
#undef DBG
#endif
#ifdef DEBUG
#undef DEBUG
#endif

static int      debug_level = 0;
#define DEBUG 1
#if DEBUG
#define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
#else
#define DBG(lvl, f)
#endif

#define ABS(x) ((x) > 0 ? (x) : -(x))

/******************************************************************************
 * WacomDeviceRec flags
 *****************************************************************************/
#define DEVICE_ID(flags) ((flags) & 0x07)

#define STYLUS_ID       1
#define CURSOR_ID       2
#define ERASER_ID       4
#define ABSOLUTE_FLAG         8
#define FIRST_TOUCH_FLAG      16
#define     KEEP_SHAPE_FLAG         32
#define BAUD_19200_FLAG       64

/******************************************************************************
 * WacomCommonRec flags
 *****************************************************************************/
#define TILT_FLAG 1
#define GRAPHIRE_FLAG   2

typedef struct
{
    int           state;
    int           coord[3];
    int           tilt[3];
} WacomFilterState;

typedef struct
{
    int                 device_id;
    int                 device_type;
    unsigned int  serial_num;
    int                 x;
    int                 y;
    int                 buttons;
    int                 pressure;
    int                 tiltx;
    int                 tilty;
    int                 rotation;
    int                 wheel;
    int                 discard_first;
    int                 proximity;
    WacomFilterState    x_filter;
    WacomFilterState    y_filter;
} WacomDeviceState;

#define PEN(ds)         (((ds->device_id) & 0x07ff) == 0x0022)
#define STROKING_PEN(ds) (((ds->device_id) & 0x07ff) == 0x0032)
#define AIRBRUSH(ds)    (((ds->device_id) & 0x07ff) == 0x0112)
#define MOUSE_4D(ds)    (((ds->device_id) & 0x07ff) == 0x0094)
#define LENS_CURSOR(ds) (((ds->device_id) & 0x07ff) == 0x0096)
#define INKING_PEN(ds)  (((ds->device_id) & 0x07ff) == 0x0012)

typedef struct
{
    /* configuration fields */
    unsigned char flags;            /* various flags (device type, absolute, first touch...) */
    int                 topX;       /* X top */
    int                 topY;       /* Y top */
    int                 bottomX;    /* X bottom */
    int                 bottomY;    /* Y bottom */
    double        factorX;    /* X factor */
    double        factorY;    /* Y factor */
    unsigned int  serial;             /* device serial number */
    int                 initNumber;     /* magic number for the init phasis */
    int                 screen_no;  /* associated screen */
    
    struct _WacomCommonRec *common; /* common info pointer */
    
    /* state fields */
    int                 oldX;       /* previous X position */
    int                 oldY;       /* previous Y position */
    int                 oldZ;       /* previous pressure */
    int                 oldTiltX;   /* previous tilt in x direction */
    int                 oldTiltY;   /* previous tilt in y direction */    
    int                 oldWheel;   /* previous wheel value */    
    int                 oldButtons; /* previous buttons state */
    int                 oldProximity;     /* previous proximity */
} WacomDeviceRec, *WacomDevicePtr;

typedef struct _WacomCommonRec 
{
    char          *wcmDevice; /* device file name */
    int                 wcmSuppress;      /* transmit position if increment is superior */
    unsigned char wcmFlags;   /* various flags (handle tilt) */
    int                 wcmMaxX;    /* max X value */
    int                 wcmMaxY;    /* max Y value */
    int                 wcmMaxZ;    /* max Z value */
    int                 wcmResolX;  /* X resolution in points/inch */
    int                 wcmResolY;  /* Y resolution in points/inch */
    int                 wcmResolZ;  /* Z resolution in points/inch */
    LocalDevicePtr      *wcmDevices;      /* array of all devices sharing the same port */
    int                 wcmNumDevices;    /* number of devices */
    int                 wcmIndex;   /* number of bytes read */
    int                 wcmPktLength;     /* length of a packet */
    unsigned char wcmData[9]; /* data read on the device */
    Bool          wcmHasEraser;     /* True if an eraser has been configured */
    Bool          wcmStylusSide;    /* eraser or stylus ? */
    Bool          wcmStylusProximity; /* the stylus is in proximity ? */
    int                 wcmProtocolLevel; /* 4 for Wacom IV, 5 for Wacom V */
    int                 wcmThreshold;     /* Threshold for counting pressure as a button */
    WacomDeviceState    wcmDevStat[2];    /* device state for each tool */
    int                 wcmInitNumber;  /* magic number for the init phasis */
    unsigned int  wcmLinkSpeed;   /* serial link speed */
    Bool          (*wcmOpen)(LocalDevicePtr /*local*/); /* function used to open the line (serial or USB) */
    unsigned int  wcmLastSerial;    /* last device (used by the USB part) */
} WacomCommonRec, *WacomCommonPtr;

/******************************************************************************
 * configuration stuff
 *****************************************************************************/
#define CURSOR_SECTION_NAME "wacomcursor"
#define STYLUS_SECTION_NAME "wacomstylus"
#define ERASER_SECTION_NAME "wacomeraser"


/******************************************************************************
 * constant and macros declarations
 *****************************************************************************/
#define DEFAULT_MAXZ 240      /* default value of MaxZ when nothing is configured */
#define BUFFER_SIZE 256       /* size of reception buffer */
#define XI_STYLUS "STYLUS"    /* X device name for the stylus */
#define XI_CURSOR "CURSOR"    /* X device name for the cursor */
#define XI_ERASER "ERASER"    /* X device name for the eraser */
#define MAX_VALUE 100           /* number of positions */
#define MAXTRY 3                /* max number of try to receive magic number */
#define MAX_COORD_RES 1270.0  /* Resolution of the returned MaxX and MaxY */
#define INVALID_THRESHOLD 30000 /* Invalid threshold value used to test if the user
                         * configured it or not */

#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))

#define WC_RESET  "\r#" /* reset to wacom IV command set or wacom V reset */
#define WC_RESET_BAUD   "\r$" /* reset baud rate to default (wacom V) or switch to wacom IIs (wacom IV) */
#define WC_CONFIG "~R\r"      /* request a configuration string */
#define WC_COORD  "~C\r"      /* request max coordinates */
#define WC_MODEL  "~#\r"      /* request model and ROM version */

#define WC_MULTI  "MU1\r"     /* multi mode input */
#define WC_UPPER_ORIGIN "OC1\r"     /* origin in upper left */
#define WC_SUPPRESS     "SU"  /* suppress mode */
#define WC_ALL_MACRO    "~M0\r"     /* enable all macro buttons */
#define WC_NO_MACRO1    "~M1\r"     /* disable macro buttons of group 1 */
#define WC_RATE   "IT0\r"     /* max transmit rate (unit of 5 ms) */
#define WC_TILT_MODE    "FM1\r"     /* enable extra protocol for tilt management */
#define WC_NO_INCREMENT "IN0\r"     /* do not enable increment mode */
#define WC_STREAM_MODE  "SR\r"      /* enable continuous mode */
#define WC_PRESSURE_MODE "PH1\r" /* enable pressure mode */
#define WC_ZFILTER      "ZF1\r" /* stop sending coordinates */
#define WC_STOP         "\nSP\r" /* stop sending coordinates */
#define WC_START  "ST\r"      /* start sending coordinates */
#define WC_NEW_RESOLUTION "NR"      /* change the resolution */

static const char * setup_string = WC_MULTI WC_UPPER_ORIGIN
 WC_ALL_MACRO WC_NO_MACRO1 WC_RATE WC_NO_INCREMENT WC_STREAM_MODE WC_ZFILTER;

static const char * penpartner_setup_string = WC_PRESSURE_MODE WC_START;

#define WC_V_SINGLE     "MT0\r"
#define WC_V_MULTI      "MT1\r"
#define WC_V_ID         "ID1\r"
#define WC_V_19200      "BA19\r"
#define WC_V_38400      "BA38\r"
/*  #define WC_V_9600   "BA96\r" */
#define WC_V_9600 "$\r"

#define WC_RESET_19200  "\r$" /* reset to 9600 baud */
#define WC_RESET_19200_IV "\r#"

static const char * intuos_setup_string = WC_V_MULTI WC_V_ID WC_RATE;

#define COMMAND_SET_MASK      0xc0
#define BAUD_RATE_MASK        0x0a
#define PARITY_MASK           0x30
#define DATA_LENGTH_MASK      0x40
#define STOP_BIT_MASK         0x80

#define HEADER_BIT      0x80
#define ZAXIS_SIGN_BIT  0x40
#define ZAXIS_BIT       0x04
#define ZAXIS_BITS      0x3f
#define POINTER_BIT     0x20
#define PROXIMITY_BIT   0x40
#define BUTTON_FLAG     0x08
#define BUTTONS_BITS    0x78
#define TILT_SIGN_BIT   0x40
#define TILT_BITS 0x3f

/* defines to discriminate second side button and the eraser */
#define ERASER_PROX     4
#define OTHER_PROX      1

#define HANDLE_TILT(comm) ((comm)->wcmPktLength == 9)

#define mils(res) (res * 100 / 2.54) /* resolution */

/******************************************************************************
 * Function/Macro keys variables
 *****************************************************************************/
static KeySym wacom_map[] = 
{
    NoSymbol,     /* 0x00 */
    NoSymbol,     /* 0x01 */
    NoSymbol,     /* 0x02 */
    NoSymbol,     /* 0x03 */
    NoSymbol,     /* 0x04 */
    NoSymbol,     /* 0x05 */
    NoSymbol,     /* 0x06 */
    NoSymbol,     /* 0x07 */
    XK_F1,  /* 0x08 */
    XK_F2,  /* 0x09 */
    XK_F3,  /* 0x0a */
    XK_F4,  /* 0x0b */
    XK_F5,  /* 0x0c */
    XK_F6,  /* 0x0d */
    XK_F7,  /* 0x0e */
    XK_F8,  /* 0x0f */
    XK_F8,  /* 0x10 */
    XK_F10, /* 0x11 */
    XK_F11, /* 0x12 */
    XK_F12, /* 0x13 */
    XK_F13, /* 0x14 */
    XK_F14, /* 0x15 */
    XK_F15, /* 0x16 */
    XK_F16, /* 0x17 */
    XK_F17, /* 0x18 */
    XK_F18, /* 0x19 */
    XK_F19, /* 0x1a */
    XK_F20, /* 0x1b */
    XK_F21, /* 0x1c */
    XK_F22, /* 0x1d */
    XK_F23, /* 0x1e */
    XK_F24, /* 0x1f */
    XK_F25, /* 0x20 */
    XK_F26, /* 0x21 */
    XK_F27, /* 0x22 */
    XK_F28, /* 0x23 */
    XK_F29, /* 0x24 */
    XK_F30, /* 0x25 */
    XK_F31, /* 0x26 */
    XK_F32  /* 0x27 */
};

/* minKeyCode = 8 because this is the min legal key code */
static KeySymsRec wacom_keysyms = {
  /* map    minKeyCode  maxKC width */
  wacom_map,      8,          0x27, 1
};

/******************************************************************************
 * external declarations
 *****************************************************************************/

#ifdef LINUX_INPUT
static void xf86WcmReadUSBInput(LocalDevicePtr);
static Bool xf86WcmUSBOpen(LocalDevicePtr);
#endif

#if NeedFunctionPrototypes
static LocalDevicePtr xf86WcmAllocateStylus(void);
static LocalDevicePtr xf86WcmAllocateCursor(void);
static LocalDevicePtr xf86WcmAllocateEraser(void);
#endif

/*
 ***************************************************************************
 *
 * send_request --
 *
 ***************************************************************************
 */
/*
 * send a request and wait for the answer.
 * the answer must begin with the first two chars of the request and must end
 * with \r. The last character in the answer string (\r) is replaced by a \0.
 */
static char *
send_request(int  fd,
           char   *request,
           char   *answer)
{
    int     len, nr;
    int     maxtry = MAXTRY;
  
    /* send request string */
    do {
      SYSCALL(len = write(fd, request, strlen(request)));
      if ((len == -1) && (errno != EAGAIN)) {
          ErrorF("Wacom write error : %s", strerror(errno));
          return NULL;
      }
      maxtry--;
    } while ((len == -1) && maxtry);

    if (maxtry == 0) {
      ErrorF("Wacom unable to write request string '%s' after %d tries\n", request, MAXTRY);
      return NULL;
    }
  
    do {
      maxtry = MAXTRY;
    
      /* Read the first byte of the answer which must be equal to the first
       * byte of the request.
       */
      do {    
          if ((nr = wait_for_fd(fd)) > 0) {
            SYSCALL(nr = read(fd, answer, 1));
            if ((nr == -1) && (errno != EAGAIN)) {
                ErrorF("Wacom read error : %s\n", strerror(errno));
                return NULL;
            }
            DBG(10, ErrorF("%c err=%d [0]\n", answer[0], nr));
          }
          maxtry--;  
      } while ((answer[0] != request[0]) && maxtry);

      if (maxtry == 0) {
          ErrorF("Wacom unable to read first byte of request '%c%c' answer after %d tries\n",
               request[0], request[1], MAXTRY);
          return NULL;
      }

      /* Read the second byte of the answer which must be equal to the second
       * byte of the request.
       */
      do {    
          maxtry = MAXTRY;
          do {    
            if ((nr = wait_for_fd(fd)) > 0) {
                SYSCALL(nr = read(fd, answer+1, 1));
                if ((nr == -1) && (errno != EAGAIN)) {
                  ErrorF("Wacom read error : %s\n", strerror(errno));
                  return NULL;
                }
                DBG(10, ErrorF("%c err=%d [1]\n", answer[1], nr));
            }
            maxtry--;  
          } while ((nr <= 0) && maxtry);
      
          if (maxtry == 0) {
            ErrorF("Wacom unable to read second byte of request '%c%c' answer after %d tries\n",
                   request[0], request[1], MAXTRY);
            return NULL;
          }

          if (answer[1] != request[1])
            answer[0] = answer[1];
      
      } while ((answer[0] == request[0]) &&
             (answer[1] != request[1]));

    } while ((answer[0] != request[0]) &&
           (answer[1] != request[1]));

    /* Read until carriage return or timeout (to handle broken protocol
     * implementations which don't end with a <cr>).
     */
    len = 2;
    maxtry = MAXTRY;
    do {    
      do {    
          if ((nr = wait_for_fd(fd)) > 0) {
            SYSCALL(nr = read(fd, answer+len, 1));
            if ((nr == -1) && (errno != EAGAIN)) {
                ErrorF("Wacom read error : %s\n", strerror(errno));
                return NULL;
            }
            DBG(10, ErrorF("%c err=%d [%d]\n", answer[len], nr, len));
          }
          else {
            DBG(10, ErrorF("timeout remains %d tries\n", maxtry));
            maxtry--;
          }
      } while ((nr <= 0) && maxtry);

      if (nr > 0) {
          len += nr;
      }
      
      if (maxtry == 0) {
          ErrorF("Wacom unable to read last byte of request '%c%c' answer after %d tries\n",
               request[0], request[1], MAXTRY);
          break;
      }
    } while (answer[len-1] != '\r');

    if (len <= 3)
      return NULL;
    
    answer[len-1] = '\0';
  
    return answer;
}

/*
 ***************************************************************************
 *
 * xf86WcmConvert --
 *    Convert valuators to X and Y.
 *
 ***************************************************************************
 */
static Bool
xf86WcmConvert(LocalDevicePtr local,
             int        first,
             int        num,
             int        v0,
             int        v1,
             int        v2,
             int        v3,
             int        v4,
             int        v5,
             int*       x,
             int*       y)
{
    WacomDevicePtr      priv = (WacomDevicePtr) local->private;
    int                 width, height;
    
    DBG(6, ErrorF("xf86WcmConvert\n"));

    if (first != 0 || num == 1)
      return FALSE;

    if (priv->screen_no != -1) {
      width = screenInfo.screens[priv->screen_no]->width;
      height = screenInfo.screens[priv->screen_no]->height;
    } else {
      width = miPointerCurrentScreen()->width;
      height = miPointerCurrentScreen()->height;
    }
    
    priv->factorX = ((double) width)
      / (priv->bottomX - priv->topX);
    priv->factorY = ((double) height)
      / (priv->bottomY - priv->topY);
    
    *x = v0 * priv->factorX + 0.5;
    *y = v1 * priv->factorY + 0.5;

    DBG(6, ErrorF("Wacom converted v0=%d v1=%d to x=%d y=%d\n",
              v0, v1, *x, *y));
    if (priv->screen_no != -1) {
      xf86XInputSetScreen(local, priv->screen_no, *x, *y);
    }
    return TRUE;
}

/*
 ***************************************************************************
 *
 * xf86WcmReverseConvert --
 *    Convert X and Y to valuators.
 *
 ***************************************************************************
 */
static Bool
xf86WcmReverseConvert(LocalDevicePtr      local,
                  int         x,
                  int         y,
                  int         *valuators)
{
    WacomDevicePtr      priv = (WacomDevicePtr) local->private;

    priv->factorX = ((double) miPointerCurrentScreen()->width)
      / (priv->bottomX - priv->topX);
    priv->factorY = ((double) miPointerCurrentScreen()->height)
      / (priv->bottomY - priv->topY);
    
    valuators[0] = x / priv->factorX + 0.5;
    valuators[1] = y / priv->factorY + 0.5;

    DBG(6, ErrorF("Wacom converted x=%d y=%d to v0=%d v1=%d\n", x, y,
              valuators[0], valuators[1]));

    return TRUE;
}
 
/*
 ***************************************************************************
 *
 * xf86WcmSendButtons --
 *    Send button events by comparing the current button mask with the
 *      previous one.
 *
 ***************************************************************************
 */
static void
xf86WcmSendButtons(LocalDevicePtr   local,
               int                  buttons,
               int                  rx,
               int                  ry,
               int                  rz,
               int                  rtx,
               int                  rty,
               int                  rwheel)
               
{
    int             button;
    WacomDevicePtr  priv = (WacomDevicePtr) local->private;

    for (button=1; button<=16; button++) {
      int mask = 1 << (button-1);
      
      if ((mask & priv->oldButtons) != (mask & buttons)) {
          DBG(4, ErrorF("xf86WcmSendButtons button=%d state=%d\n", 
                    button, (buttons & mask) != 0));
          xf86PostButtonEvent(local->dev, 
                        (priv->flags & ABSOLUTE_FLAG),
                        button, (buttons & mask) != 0,
                        0, 6, rx, ry, rz, rtx, rty, rwheel);
      }
    }
}

/*
 ***************************************************************************
 *
 * xf86WcmSendEvents --
 *    Send events according to the device state.
 *
 ***************************************************************************
 */
static void
xf86WcmSendEvents(LocalDevicePtr    local,
              int             type,
              unsigned int          serial,
              int             is_stylus,
              int             is_button,
              int             is_proximity,
              int             x,
              int             y,
              int             z,
              int             buttons,
              int             tx,
              int             ty,
              int             wheel)
{
    WacomDevicePtr      priv = (WacomDevicePtr) local->private;
    WacomCommonPtr      common = priv->common;
    int                 rx, ry, rz, rtx, rty, rwheel;
    int                 is_core_pointer, is_absolute;

    if ((DEVICE_ID(priv->flags) != type) ||
      ((common->wcmProtocolLevel == 5) &&
       priv->serial && (serial != priv->serial))) {
      DBG(7,
          {if (common->wcmProtocolLevel == 5) {
          ErrorF("xf86WcmSendEvents not the same device id (%u,%u)\n",
               serial, priv->serial);
          } else {
            ErrorF("xf86WcmSendEvents not the same device type (%u,%u)\n",
               DEVICE_ID(priv->flags), type);}});
      return;
    }
    
    DBG(7, ErrorF("[%s] prox=%s\tx=%d\ty=%d\tz=%d\tbutton=%s\tbuttons=%d\ttx=%d ty=%d\twl=%d\n",
              (type == STYLUS_ID) ? "stylus" : (type == CURSOR_ID) ? "cursor" : "eraser",
              is_proximity ? "true" : "false",
              x, y, z,
              is_button ? "true" : "false", buttons,
              tx, ty, wheel));

    /* Translate coordinates according to Top and Bottom points
     * if we are outside the zone do as a ProximityOut event.
     */

    if (x > priv->bottomX) {
      is_proximity = FALSE;
      buttons = 0;
      x = priv->bottomX;
    }
          
    if (y > priv->bottomY) {
      is_proximity = FALSE;
      buttons = 0;
      y = priv->bottomY;
    }

    DBG(10, ErrorF("topX=%d topY=%d\n", priv->topX, priv->topY));

    x = x - priv->topX;
    y = y - priv->topY;

    if (x < 0) {
      is_proximity = FALSE;
      buttons = 0;
      x = 0;
    }
    
    if (y < 0) {
      is_proximity = FALSE;
      buttons = 0;
      y = 0;
    }
    
    is_absolute = (priv->flags & ABSOLUTE_FLAG);
    is_core_pointer = xf86IsCorePointer(local->dev);

    DBG(6, ErrorF("[%s] %s prox=%s\tx=%d\ty=%d\tz=%d\tbutton=%s\tbuttons=%d\n",
              is_stylus ? "stylus" : "cursor",
              is_absolute ? "abs" : "rel",
              is_proximity ? "true" : "false",
              x, y, z,
              is_button ? "true" : "false", buttons));

    /* Hardware filtering isn't working on Graphire so we do it here.
     */
    if ((common->wcmFlags & GRAPHIRE_FLAG) &&
      ((is_proximity && priv->oldProximity) ||
       ((is_proximity == 0) && (priv->oldProximity == 0))) &&
      (buttons == priv->oldButtons) &&
      (ABS(x - priv->oldX) <= common->wcmSuppress) &&
      (ABS(y - priv->oldY) <= common->wcmSuppress) &&
       (ABS(z - priv->oldZ) < 3) &&
      (ABS(tx - priv->oldTiltX) < 3) &&
      (ABS(ty - priv->oldTiltY) < 3)) {
      
      DBG(10, ErrorF("Graphire filtered\n"));

      return;
    }
            
    /* sets rx and ry according to the mode */
    if (is_absolute) {
      rx = x;
      ry = y;
      rz = z;
      rtx = tx;
      rty = ty;
      rwheel = wheel;
    } else {
      rx = x - priv->oldX;
      ry = y - priv->oldY;
      rz = z - priv->oldZ;  
      rtx = tx - priv->oldTiltX;
      rty = ty - priv->oldTiltY;
      rwheel = wheel - priv->oldWheel;
    }

    /* coordinates are ready we can send events */
    if (is_proximity) {

      if (!priv->oldProximity) {
          xf86PostProximityEvent(local->dev, 1, 0, 6, rx, ry, z, tx, ty, rwheel);

          priv->flags |= FIRST_TOUCH_FLAG;
          DBG(4, ErrorF("xf86WcmSendEvents FIRST_TOUCH_FLAG set\n"));
                
          if (common->wcmProtocolLevel == 4) {
            /* handle the two sides switches in the stylus */
            if (is_stylus && (buttons == 4)) {
                priv->oldProximity = ERASER_PROX;
            }
            else {
                priv->oldProximity = OTHER_PROX;
            }
          }
          else {
            priv->oldProximity = OTHER_PROX;
          }
      }

      if (common->wcmProtocolLevel == 4 &&
          !(common->wcmFlags & GRAPHIRE_FLAG)) {
          /* The stylus reports button 4 for the second side
           * switch and button 4/5 for the eraser tip. We know
           * how to choose when we come in proximity for the
           * first time. If we are in proximity and button 4 then
           * we have the eraser else we have the second side
           * switch.
           */
          if (is_stylus) {
            if (buttons == 4) {
                buttons = (priv->oldProximity == ERASER_PROX) ? 0 : 3;
            }
            else {
                if (priv->oldProximity == ERASER_PROX && buttons == 5) {
                  buttons = ((DEVICE_ID(priv->flags) == ERASER_ID) ? 1 : 4);
                }
            }
          }
          else {
            /* If the button flag is pressed, but the switch state
             * is zero, this means that cursor button 16 was pressed
             */
            if (is_button && buttons == 0) {
                buttons = 16;
            }
          }
      }
      DBG(4, ErrorF("xf86WcmSendEvents %s rx=%d ry=%d rz=%d buttons=%d\n",
                  is_stylus ? "stylus" : "cursor", rx, ry, rz, buttons));
      
      /* Turn button index reported into a bit mask for WACOM IV.
       * The WACOM V and Graphire models already report buttons
       * as a bit mask.
       */
      if (common->wcmProtocolLevel == 4 &&
          !(common->wcmFlags & GRAPHIRE_FLAG)) {
          buttons = 1 << (buttons - 1);
      }
      
      if ((priv->oldX != x) ||
          (priv->oldY != y) ||
          (priv->oldZ != z) ||
          (is_stylus && HANDLE_TILT(common) &&
           (tx != priv->oldTiltX || ty != priv->oldTiltY))) {
          if (!is_absolute && (priv->flags & FIRST_TOUCH_FLAG)) {
            priv->flags -= FIRST_TOUCH_FLAG;
            DBG(4, ErrorF("xf86WcmSendEvents FIRST_TOUCH_FLAG unset\n"));
          } else {
            xf86PostMotionEvent(local->dev, is_absolute, 0, 6, rx, ry, rz,
                            rtx, rty, rwheel); 
          }
      }

      if (priv->oldButtons != buttons) {
          xf86WcmSendButtons (local, buttons, rx, ry, rz, rtx, rty, rwheel);
      }

      /* Simulate buttons 4 and 5 for Graphire wheel */
      if ((common->wcmProtocolLevel == 4) &&
          !is_stylus &&
          (common->wcmFlags & GRAPHIRE_FLAG) &&
          (wheel != 0)) {
          int fake_button = (wheel > 0) ? 5 : 4;

          xf86PostButtonEvent(local->dev,
                        (priv->flags & ABSOLUTE_FLAG),
                        fake_button, 1,
                        0, 6, rx, ry, rz, rtx, rty, rwheel);
          
          xf86PostButtonEvent(local->dev,
                        (priv->flags & ABSOLUTE_FLAG),
                        fake_button, 0,
                        0, 6, rx, ry, rz, rtx, rty, rwheel);
      }
      
      priv->oldButtons = buttons;
      priv->oldX = x;
      priv->oldY = y;
      priv->oldZ = z;
      priv->oldTiltX = tx;
      priv->oldTiltY = ty;
      priv->oldWheel = wheel;
    }
    else { /* !PROXIMITY */
      /* reports button up when the device has been down and becomes out of proximity */
      if (priv->oldButtons) {
          xf86WcmSendButtons (local, 0, rx, ry, rz, rtx, rty, rwheel);
          priv->oldButtons = 0;
      }
      if (!is_core_pointer) {
          /* macro button management */
          if (common->wcmProtocolLevel == 4 && buttons) {
            int   macro = z / 2;

            DBG(6, ErrorF("macro=%d buttons=%d wacom_map[%d]=%x\n",
                        macro, buttons, macro, wacom_map[macro]));

            /* First available Keycode begins at 8 => macro+7 */
            xf86PostKeyEvent(local->dev, macro+7, 1,
                         is_absolute, 0, 6,
                         0, 0, buttons, rtx, rty, rwheel);
            xf86PostKeyEvent(local->dev, macro+7, 0,
                         is_absolute, 0, 6,
                         0, 0, buttons, rtx, rty, rwheel);
          }
          if (priv->oldProximity) {
            xf86PostProximityEvent(local->dev, 0, 0, 6, rx, ry, rz,
                               rtx, rty, rwheel);
          }
      }
      priv->oldProximity = 0;
    }
}

/*
 ***************************************************************************
 *
 * xf86WcmSuppress --
 *    Determine whether device state has changed enough - return 1
 *    if not.
 *
 ***************************************************************************
 */
static int
xf86WcmSuppress(int                 suppress,
            WacomDeviceState  *ds1,
            WacomDeviceState  *ds2)
{
    if (ds1->buttons != ds2->buttons) return 0;
    if (ds1->proximity != ds2->proximity) return 0;
    if (ABS(ds1->x - ds2->x) >= suppress) return 0;
    if (ABS(ds1->y - ds2->y) >= suppress) return 0;
    if (ABS(ds1->pressure - ds2->pressure) >= suppress) return 0;
    if ((1800 + ds1->rotation - ds2->rotation) % 1800 >= suppress &&
      (1800 + ds2->rotation - ds1->rotation) % 1800 >= suppress) return 0;
    if (ABS(ds1->wheel - ds2->wheel) >= suppress) return 0;
    return 1;
}

/*
 ***************************************************************************
 *
 * xf86WcmIntuosFilter --
 *     Correct some hardware defects we've been seeing in Intuos pads,
 *       but also cuts down quite a bit on jitter.
 *
 ***************************************************************************
 */
static int
xf86WcmIntuosFilter(WacomFilterState      *state,
                int                 coord,
                int                 tilt)
{
    int tilt_filtered;
    int ts;
    int x0_pred;
    int x0_pred1;
    int x0, x1, x2, x3;
    int x;
    
    tilt_filtered = tilt + state->tilt[1] + state->tilt[2] + state->tilt[3];
    state->tilt[2] = state->tilt[1];
    state->tilt[1] = state->tilt[0];
    state->tilt[0] = tilt;
    
    x0 = coord;
    x1 = state->coord[0];
    x2 = state->coord[1];
    x3 = state->coord[2];
    state->coord[0] = x0;
    state->coord[1] = x1;
    state->coord[2] = x2;
    
    ts = tilt_filtered >= 0 ? 1 : -1;
    
    if (state->state == 0 || state->state == 3) {
      x0_pred = 2 * x1 - x2;
      x0_pred1 = 3 * x2 - 2 * x3;
      if (ts * (x0 - x0_pred) > 12 &&
          ts * (x0 - x0_pred1) > 12) {
          /* detected a jump at x0 */
          state->state = 1;
          x = x1;
      }
      else if (state->state == 0) {
          x = (7 * x0 + 14 * x1 + 15 * x2 - 4 * x3 + 16) >> 5;
      }
      else { /* state->state == 3 */
          /* a jump at x3 was detected */
          x = (x0 + 2 * x1 + x2 + 2) >> 2;
          state->state = 0;
      }
    }
    else if (state->state == 1) {
      /* a jump at x1 was detected */
      x = (3 * x0 + 7 * x2 - 2 * x3 + 4) >> 3;
      state->state = 2;
    }
    else { /* state->state == 2 */
      /* a jump at x2 was detected */
      x = x1;
      state->state = 3;
    }

    return x;
}

/*
 ***************************************************************************
 *
 * xf86WcmReadInput --
 *    Read the new events from the device, and enqueue them.
 *
 ***************************************************************************
 */
static void
xf86WcmReadInput(LocalDevicePtr         local)
{
    WacomDevicePtr      priv = (WacomDevicePtr) local->private;
    WacomCommonPtr      common = priv->common;
    int                 len, loop, idx;
    int                 is_stylus = 1, is_button, is_proximity, wheel=0;
    int                 is_absolute = (priv->flags & ABSOLUTE_FLAG);
    int                 x, y, z, buttons, tx = 0, ty = 0;
    unsigned char buffer[BUFFER_SIZE];
    WacomDeviceState    *ds;
    WacomDeviceState    old_ds;
    int                 have_data;
  
    DBG(7, ErrorF("xf86WcmReadInput BEGIN device=%s fd=%d\n",
              common->wcmDevice, local->fd));

    SYSCALL(len = read(local->fd, buffer, sizeof(buffer)));

    if (len <= 0) {
      ErrorF("Error reading wacom device : %s\n", strerror(errno));
      return;
    } else {
      DBG(10, ErrorF("xf86WcmReadInput read %d bytes\n", len));
    }

    for(loop=0; loop<len; loop++) {

      /* Format of 7 bytes data packet for Wacom Tablets
      Byte 1
      bit 7  Sync bit always 1
      bit 6  Pointing device detected
      bit 5  Cursor = 0 / Stylus = 1
      bit 4  Reserved
      bit 3  1 if a button on the pointing device has been pressed
      bit 2  Reserved
      bit 1  X15
      bit 0  X14

      Byte 2
      bit 7  Always 0
      bits 6-0 = X13 - X7

      Byte 3
      bit 7  Always 0
      bits 6-0 = X6 - X0

      Byte 4
      bit 7  Always 0
      bit 6  B3
      bit 5  B2
      bit 4  B1
      bit 3  B0
      bit 2  P0
      bit 1  Y15
      bit 0  Y14

      Byte 5
      bit 7  Always 0
      bits 6-0 = Y13 - Y7

      Byte 6
      bit 7  Always 0
      bits 6-0 = Y6 - Y0

      Byte 7
      bit 7 Always 0
      bit 6  Sign of pressure data
      bit 5  P6
      bit 4  P5
      bit 3  P4
      bit 2  P3
      bit 1  P2
      bit 0  P1

      byte 8 and 9 are optional and present only
      in tilt mode.

      Byte 8
      bit 7 Always 0
      bit 6 Sign of tilt X
      bit 5  Xt6
      bit 4  Xt5
      bit 3  Xt4
      bit 2  Xt3
      bit 1  Xt2
      bit 0  Xt1
       
      Byte 9
      bit 7 Always 0
      bit 6 Sign of tilt Y
      bit 5  Yt6
      bit 4  Yt5
      bit 3  Yt4
      bit 2  Yt3
      bit 1  Yt2
      bit 0  Yt1
       
      */

      if ((common->wcmIndex == 0) && !(buffer[loop] & HEADER_BIT)) { /* magic bit is not OK */
          DBG(6, ErrorF("xf86WcmReadInput bad magic number 0x%x (pktlength=%d) %d\n",
                    buffer[loop], common->wcmPktLength, loop));
          continue;
      }
      else { /* magic bit at wrong place */
          if ((common->wcmIndex != 0) && (buffer[loop] & HEADER_BIT)) {
            DBG(6, ErrorF("xf86WcmReadInput magic number 0x%x detetected at index %d loop=%d\n",
                        (unsigned int) buffer[loop], common->wcmIndex, loop));
            common->wcmIndex = 0;
          }
      }
      
      common->wcmData[common->wcmIndex++] = buffer[loop];

      if (common->wcmProtocolLevel == 4 &&
          common->wcmIndex == common->wcmPktLength) {
          int     is_graphire = common->wcmFlags & GRAPHIRE_FLAG;
          
          /* the packet is OK */

          /* reset char count for next read */
          common->wcmIndex = 0;

          x = (((common->wcmData[0] & 0x3) << 14) +
             (common->wcmData[1] << 7) +
             common->wcmData[2]);
          y = (((common->wcmData[3] & 0x3) << 14) +
             (common->wcmData[4] << 7) +
             common->wcmData[5]);

          /* check which device we have */
          is_stylus = (common->wcmData[0] & POINTER_BIT);
            
          z = ((common->wcmData[6] & ZAXIS_BITS) * 2) +
            ((common->wcmData[3] & ZAXIS_BIT) >> 2);

          if (common->wcmMaxZ == 512) {
            z = z*4 + ((common->wcmData[0] & ZAXIS_BIT) >> 1);

            if (!(common->wcmData[6] & ZAXIS_SIGN_BIT)) {
                z += 256;
            }
            DBG(10, ErrorF("graphire pressure(%c)=%d\n",
                         (common->wcmData[6] & ZAXIS_SIGN_BIT) ? '-' : '+', z));
          }
          else {
            if (!(common->wcmData[6] & ZAXIS_SIGN_BIT)) {
                z += (common->wcmMaxZ / 2);
            }
          }
          
          is_proximity = (common->wcmData[0] & PROXIMITY_BIT);

          if (is_graphire) {
            if (is_stylus) {
                buttons = ((common->wcmData[3] & 0x30) >> 3) |
                  (z >= common->wcmThreshold ? 1 : 0);
            }
            else {
                buttons = (common->wcmData[3] & 0x38) >> 3;

                wheel = (common->wcmData[6] & 0x30) >> 4;
            
                if (common->wcmData[6] & 0x40) {
                  wheel = -wheel;
                }
            }
            is_button = (buttons != 0);

            DBG(10, ErrorF("graphire buttons=%d prox=%d wheel=%d\n", buttons, is_proximity, wheel));
          }
          else {
            is_button = (common->wcmData[0] & BUTTON_FLAG);
            buttons = (common->wcmData[3] & BUTTONS_BITS) >> 3;
          }
          
          /* The stylus reports button 4 for the second side
           * switch and button 4/5 for the eraser tip. We know
           * how to choose when we come in proximity for the
           * first time. If we are in proximity and button 4 then
           * we have the eraser else we have the second side
           * switch.
           */
          if (is_stylus) {
            if (!common->wcmStylusProximity && is_proximity) {
                if (is_graphire) {
                      common->wcmStylusSide = !(common->wcmData[3] & 0x40);
                }
                else {
                  common->wcmStylusSide = (buttons != 4);
                }
            }
            DBG(8, ErrorF("xf86WcmReadInput %s side\n",
                        common->wcmStylusSide ? "stylus" : "eraser"));
            common->wcmStylusProximity = is_proximity;

            /* handle tilt values only for stylus */
            if (HANDLE_TILT(common)) {
                tx = (common->wcmData[7] & TILT_BITS);
                ty = (common->wcmData[8] & TILT_BITS);
                if (common->wcmData[7] & TILT_SIGN_BIT)
                  tx -= (TILT_BITS + 1);
                if (common->wcmData[8] & TILT_SIGN_BIT)
                  ty -= (TILT_BITS + 1);
            }
          }

          for(idx=0; idx<common->wcmNumDevices; idx++) {
              LocalDevicePtr  local_dev = common->wcmDevices[idx];
            WacomDevicePtr    priv = (WacomDevicePtr) local_dev->private;
            int         temp_buttons = buttons;
            int         temp_is_proximity = is_proximity;
            int             curDevice;
            
            DBG(7, ErrorF("xf86WcmReadInput trying to send to %s\n",
                        local_dev->name));
            
            /* check for device type (STYLUS, ERASER or CURSOR) */
            
            if (is_stylus) {
                /*
                 * The eraser is reported as button 4 and 5 of the stylus.
                 * if we haven't an independent device for the eraser
                 * report the button as button 3 of the stylus.
                 */
                if (is_proximity) {
                  if (is_graphire) {
                      if (common->wcmData[3] & 0x40) {
                        curDevice = ERASER_ID;
                      }
                      else {
                        curDevice = STYLUS_ID;
                      }
                  }
                  else {
                      if ((buttons & 4) && common->wcmHasEraser &&
                        ((!priv->oldProximity ||
                          (priv->oldProximity == ERASER_PROX)))) {
                        curDevice = ERASER_ID;
                      } else {
                        curDevice = STYLUS_ID;
                      }
                  }
                } else {
                  /*
                   * When we are out of proximity with the eraser the
                   * button 4 isn't reported so we must check the
                   * previous proximity device.
                   */
                  if (common->wcmHasEraser && (priv->oldProximity == ERASER_PROX)) {
                      curDevice = ERASER_ID;
                  } else {
                      curDevice = STYLUS_ID;
                  }
                }
                
                /* We check here to see if we changed between eraser and stylus
                 * without leaving proximity. The most likely cause is that
                 * we were fooled by the second side switch into thinking the
                 * stylus was the eraser. If this happens, we send
                 * a proximity-out for the old device.
                 */
                if ((DEVICE_ID(priv->flags) == STYLUS_ID ||
                   DEVICE_ID(priv->flags) == ERASER_ID) &&
                  curDevice != DEVICE_ID(priv->flags)) {
                  if (priv->oldProximity) {
                      curDevice = DEVICE_ID(priv->flags);
                      temp_buttons = 0;
                      temp_is_proximity = 0;
                      DBG(10, ErrorF("eraser and stylus mix\n"));
                  } else 
                      continue;
                }
                
                DBG(10, ErrorF((DEVICE_ID(priv->flags) == ERASER_ID) ? 
                           "Eraser\n" : 
                           "Stylus\n"));
            }
            else {
                if (DEVICE_ID(priv->flags) != CURSOR_ID)
                  continue;   
                DBG(10, ErrorF("Cursor\n"));
                curDevice = CURSOR_ID;
            }
            
            xf86WcmSendEvents(common->wcmDevices[idx],
                          curDevice, 0,
                          is_stylus,
                          is_button,
                          temp_is_proximity,
                          x, y, z, temp_buttons,
                          tx, ty, wheel);
          }
      }
      else if (common->wcmProtocolLevel == 5 &&
             common->wcmIndex == common->wcmPktLength) {
          /* the packet is OK */
          int x, y;

          /* reset count for read of next packet */
          common->wcmIndex = 0;

          ds = &common->wcmDevStat[common->wcmData[0] & 0x01];
          old_ds = *ds;
          have_data = 0;

          DBG(7, ErrorF("packet header = 0x%x\n",
                    (unsigned int)common->wcmData[0]));
          
          /* Device ID packet */
          if ((common->wcmData[0] & 0xfc) == 0xc0) {
            memset(ds, 0, sizeof(*ds));
            ds->proximity = 1;
            ds->device_id = (((common->wcmData[1] & 0x7f) << 5) |
                         ((common->wcmData[2] & 0x7c) >> 2));
            ds->serial_num = (((common->wcmData[2] & 0x03) << 30) |
                          ((common->wcmData[3] & 0x7f) << 23) |
                          ((common->wcmData[4] & 0x7f) << 16) |
                          ((common->wcmData[5] & 0x7f) << 9) |
                          ((common->wcmData[6] & 0x7f) << 23) |
                          ((common->wcmData[7] & 0x60) >> 5));
            if ((ds->device_id & 0xf06) != 0x802)
              ds->discard_first = 1;

            if (PEN(ds) || STROKING_PEN(ds) || INKING_PEN(ds) || AIRBRUSH(ds))
                ds->device_type = STYLUS_ID;
            else if (MOUSE_4D(ds) || LENS_CURSOR(ds))
                ds->device_type = CURSOR_ID;
            else
                ds->device_type = ERASER_ID;
            
            DBG(6, ErrorF("device_id=0x%x serial_num=%u type=%s\n",
                        ds->device_id, ds->serial_num,
                        (ds->device_type == STYLUS_ID) ? "stylus"
                        : (ds->device_type == CURSOR_ID) ? "cursor"
                        : "eraser"));
          }
          /* Out of proximity packet */
          else if ((common->wcmData[0] & 0xfe) == 0x80) {
            ds->proximity = 0;
            have_data = 1;
          }
          /* General pen packet or eraser packet or airbrush first packet */
          else if (((common->wcmData[0] & 0xb8) == 0xa0) ||
                 /* airbrush second packet */
                 ((common->wcmData[0] & 0xbe) == 0xb4)) {
            is_stylus = 1;
            ds->x = (((common->wcmData[1] & 0x7f) << 9) |
                   ((common->wcmData[2] & 0x7f) << 2) |
                   ((common->wcmData[3] & 0x60) >> 5));
            ds->y = (((common->wcmData[3] & 0x1f) << 11) |
                   ((common->wcmData[4] & 0x7f) << 4) |
                   ((common->wcmData[5] & 0x78) >> 3));
            if ((common->wcmData[0] & 0xb8) == 0xa0) {
                ds->pressure = (((common->wcmData[5] & 0x07) << 7) |
                            (common->wcmData[6] & 0x7f));
                ds->buttons = (((common->wcmData[0]) & 0x06) |
                           (ds->pressure >= common->wcmThreshold));
            }
            else {
                ds->wheel = (((common->wcmData[5] & 0x07) << 7) |
                         (common->wcmData[6] & 0x7f));
            }
            ds->tiltx = (common->wcmData[7] & TILT_BITS);
            ds->tilty = (common->wcmData[8] & TILT_BITS);
            if (common->wcmData[7] & TILT_SIGN_BIT)
              ds->tiltx -= (TILT_BITS + 1);
            if (common->wcmData[8] & TILT_SIGN_BIT)
              ds->tilty -= (TILT_BITS + 1);
            ds->proximity = (common->wcmData[0] & PROXIMITY_BIT);
            have_data = 1;
          }
          /* 4D mouse 1st packet or Lens cursor packet */
          else if (((common->wcmData[0] & 0xbe) == 0xa8) ||
                 ((common->wcmData[0] & 0xbe) == 0xb0)) {
            is_stylus = 0;
            ds->x = (((common->wcmData[1] & 0x7f) << 9) |
                 ((common->wcmData[2] & 0x7f) << 2) |
                 ((common->wcmData[3] & 0x60) >> 5));
            ds->y = (((common->wcmData[3] & 0x1f) << 11) |
                 ((common->wcmData[4] & 0x7f) << 4) |
                 ((common->wcmData[5] & 0x78) >> 3));
            ds->tilty = 0;
            ds->wheel = (((common->wcmData[5] & 0x07) << 7) |
                 (common->wcmData[6] & 0x7f));
            if (common->wcmData[8] & 0x08) ds->wheel = -ds->wheel;
            /* 4D mouse */
            if (MOUSE_4D(ds)) {
                ds->buttons = (((common->wcmData[8] & 0x70) >> 1) |
                           (common->wcmData[8] & 0x07));
                have_data = !ds->discard_first;
            }
            /* Lens cursor */
            else {
                ds->buttons = common->wcmData[8];
                have_data = 1;
            }
            ds->proximity = (common->wcmData[0] & PROXIMITY_BIT);
          }
          /* 4D mouse 2nd packet */
          else if ((common->wcmData[0] & 0xbe) == 0xaa) {
            is_stylus = 0;
            ds->x = (((common->wcmData[1] & 0x7f) << 9) |
                   ((common->wcmData[2] & 0x7f) << 2) |
                   ((common->wcmData[3] & 0x60) >> 5));
            ds->y = (((common->wcmData[3] & 0x1f) << 11) |
                   ((common->wcmData[4] & 0x7f) << 4) |
                   ((common->wcmData[5] & 0x78) >> 3));
            ds->tilty = 0;
            ds->rotation = (((common->wcmData[6] & 0x0f) << 7) |
                        (common->wcmData[7] & 0x7f));
            ds->tiltx = ((900 - ((ds->rotation + 900) % 1800)) >> 1);
            ds->proximity = (common->wcmData[0] & PROXIMITY_BIT);
            have_data = 1;
            ds->discard_first = 0;
          }
          else {
            DBG(10, ErrorF("unknown wacom V packet 0x%x\n",
                         common->wcmData[0]));
          }
          
          /* Suppress data */
          if (have_data &&
            xf86WcmSuppress(common->wcmSuppress, &old_ds, ds)) {
              DBG(10, ErrorF("Suppressing data according to filter\n"));
            *ds = old_ds;
            have_data = 0;
          }

          if (have_data) {
              if (is_absolute) {
                x = xf86WcmIntuosFilter (&ds->x_filter, ds->x, ds->tiltx);
                y = xf86WcmIntuosFilter (&ds->y_filter, ds->y, ds->tilty);
            } 
            else {
                x = ds->x;
                y = ds->y;
            }
            for(idx=0; idx<common->wcmNumDevices; idx++) {
                DBG(7, ErrorF("xf86WcmReadInput trying to send to %s\n",
                          common->wcmDevices[idx]->name));

                xf86WcmSendEvents(common->wcmDevices[idx],
                              ds->device_type, ds->serial_num,
                              is_stylus,
                              ds->buttons,
                              ds->proximity,
                              x, y,
                              ds->pressure,
                              ds->buttons,
                              ds->tiltx, ds->tilty,
                              ds->wheel);
            }
          }
      }
    }
    DBG(7, ErrorF("xf86WcmReadInput END   local=0x%x priv=0x%x index=%d\n",
              local, priv, common->wcmIndex));
}

#ifdef LINUX_INPUT
/*
 ***************************************************************************
 *
 * xf86WcmIsUSBLine --
 *    Test if the attached device is a USB one.
 *
 ***************************************************************************
 */
static int
xf86WcmIsUSBLine(int    fd)
{
    int version;
    int err;

    SYSCALL(err = ioctl(fd, EVIOCGVERSION, &version));
    
    if (!err) {
      ErrorF("%s Wacom Kernel Input driver version is %d.%d.%d\n", XCONFIG_PROBED,
             version >> 16, (version >> 8) & 0xff, version & 0xff);
      return 1;
    } else {
      return 0;
    }
}

/*
 ***************************************************************************
 *
 * xf86WcmReadUSBInput --
 *    Read the new events from the device, and enqueue them.
 *
 ***************************************************************************
 */
static void
xf86WcmReadUSBInput(LocalDevicePtr         local)
{
    WacomDevicePtr      priv = (WacomDevicePtr) local->private;
    WacomCommonPtr      common = priv->common;
    int serial = common->wcmLastSerial;
    int is_proximity = priv->oldProximity;
    int x = priv->oldX;
    int y = priv->oldY;
    int pressure = priv->oldZ;
    int buttons = priv->oldButtons;
    int tilt_x = priv->oldTiltX;
    int tilt_y = priv->oldTiltY;
    int wheel = priv->oldWheel;

    ssize_t              len;
    int                  idx;
    struct input_event * event;
    char                 eventbuf[sizeof(struct input_event) * MAX_EVENTS];
#define MOD_BUTTONS(bit, value) \
    { int _b=bit, _v=value; buttons = (((_v) != 0) ? (buttons | _b) : (buttons & ~ _b)); }

    SYSCALL(len = read(local->fd, eventbuf, sizeof(eventbuf)));

    if (len <= 0) {
      ErrorF("Error reading wacom device : %s\n", strerror(errno));
      return;
    }
    
    for (event=(struct input_event *)eventbuf;
       event<(struct input_event *)(eventbuf+len); event++) {
      DBG(10, ErrorF("xf86WcmReadUSBInput event->type=%d\n", event->type));
      
      switch (event->type) {
      case EV_ABS:
          DBG(10, ErrorF("xf86WcmReadUSBInput event->code=%d\n", event->code));
          switch (event->code) {
          case ABS_X:
            x = event->value;
            break;

          case ABS_Y:
            y = event->value;
            break;

          case ABS_TILT_X:
          case ABS_RZ:
            tilt_x = event->value;
            break;

          case ABS_TILT_Y:
            tilt_y = event->value;
            break;

          case ABS_PRESSURE:
            pressure = event->value;
            MOD_BUTTONS (1, event->value > common->wcmThreshold ? 1 : 0);
            break;

          case ABS_DISTANCE:
            /* This is not sent by the driver */
            break;

          case ABS_MISC:
            serial = event->value;
            DBG(10, ErrorF("wacom tool serial id=%d\n", serial));
            break;

          case ABS_WHEEL:
          case ABS_THROTTLE:
            wheel = event->value;
            break;

          }
          break; /* EV_ABS */

      case EV_REL:
          switch (event->code) {
          case REL_WHEEL:
            /* FIXME */
            break;
          default:
            ErrorF("wacom: relative event received (%d)!!!\n", event->code);
            break;
          }
          break; /* EV_REL */

      case EV_KEY:
          switch (event->code) {
          case BTN_TOOL_PEN:
          case BTN_TOOL_PENCIL:
          case BTN_TOOL_BRUSH:
          case BTN_TOOL_AIRBRUSH:
            DBG(10, ErrorF("USB Stylus detected %x\n", event->code));
            common->wcmIndex = STYLUS_ID;
            is_proximity = (event->value != 0);
            break;

          case BTN_TOOL_RUBBER:
            DBG(10, ErrorF("USB eraser detected %x\n", event->code));
            common->wcmIndex = ERASER_ID;
            is_proximity = (event->value != 0);
            break;

          case BTN_TOOL_MOUSE:
          case BTN_TOOL_LENS:
            DBG(10, ErrorF("USB mouse detected %x\n", event->code));
            common->wcmIndex = CURSOR_ID;
            is_proximity = (event->value != 0);
            break;

          case BTN_TOUCH:
            /* we use the pressure to determine the button 1 */
            break;

          case BTN_STYLUS:
            MOD_BUTTONS (2, event->value);
            break;

          case BTN_STYLUS2:
            MOD_BUTTONS (4, event->value);
            break;

          case BTN_LEFT:
            MOD_BUTTONS (1, event->value);
            break;

          case BTN_MIDDLE:
            MOD_BUTTONS (2, event->value);
            break;

          case BTN_RIGHT:
            MOD_BUTTONS (4, event->value);
            break;

          case BTN_SIDE:
            MOD_BUTTONS (8, event->value);
            break;

          case BTN_EXTRA:
            MOD_BUTTONS (16, event->value);
            break;
          }
          break; /* EV_KEY */
      } /* switch event->type */

      /* ABS_MISC is the event terminator */
      if (event->type != EV_ABS || event->code != ABS_MISC) {
          continue;
      }
      
      if ((is_proximity == priv->oldProximity) &&
          (buttons == priv->oldButtons) &&
          (ABS(x - priv->oldX) <= common->wcmSuppress) &&
          (ABS(y - priv->oldY) <= common->wcmSuppress) &&
          (ABS(pressure - priv->oldZ) < 3) &&
          (ABS(tilt_x - priv->oldTiltX) < 3) &&
          (ABS(tilt_y - priv->oldTiltY) < 3)) {
          DBG(10, ErrorF("filtered\n"));
          continue;
      }
    
    for (idx=0; idx<common->wcmNumDevices; idx++) {
      WacomDevicePtr    dev = common->wcmDevices[idx]->private;
      int         id;

      id  = DEVICE_ID (dev->flags);

      /* Find the device the current events are meant for */
      if (id == common->wcmIndex) {
          xf86WcmSendEvents(common->wcmDevices[idx],
                  common->wcmIndex,
                  serial,
                  (common->wcmIndex == STYLUS_ID || common->wcmIndex == ERASER_ID),
                  !!(buttons),
                  is_proximity,
                  x, y, pressure, buttons,
                  tilt_x, tilt_y, wheel
                  );
      }
    }
    priv->oldX = x;
    priv->oldY = y;
    priv->oldZ = pressure;
    priv->oldTiltX = tilt_x;
    priv->oldTiltY = tilt_y;
    priv->oldProximity = is_proximity;
    priv->oldButtons = buttons;
    priv->oldWheel = wheel;
    common->wcmLastSerial = serial;
    }
}

/*
 ***************************************************************************
 *
 * xf86WcmUSBOpen --
 *
 ***************************************************************************
 */

#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define test_bit(bit, array)  ((array[LONG(bit)] >> OFF(bit)) & 1)
#define OFF(x)  ((x)%BITS_PER_LONG)
#define LONG(x) ((x)/BITS_PER_LONG)

static Bool
xf86WcmUSBOpen(LocalDevicePtr local)
{
    int                 err = 0;
    WacomDevicePtr      priv = (WacomDevicePtr)local->private;
    WacomCommonPtr      common = priv->common;
    char          name[256] = "Unknown";
    int                 abs[5];
    unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
    int                 i, j;
    
    local->fd = xf86OpenSerial(local->options);
    if (local->fd == -1) {
      ErrorF("Error opening %s : %s\n", common->wcmDevice, strerror(errno));
      return !Success;
    }

    ioctl(local->fd, EVIOCGNAME(sizeof(name)), name);
    ErrorF("%s Wacom Kernel Input device name: \"%s\"\n", XCONFIG_PROBED, name);

    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:
                      if (common->wcmMaxX == 0) {
                        common->wcmMaxX = abs[2];
                      }
                      break;
                      
                  case ABS_Y:
                      if (common->wcmMaxY == 0) {
                        common->wcmMaxY = abs[2];
                      }
                      break;
                      
                  case ABS_Z:
                      if (common->wcmMaxZ == DEFAULT_MAXZ) {
                        common->wcmMaxZ = abs[2];
                      }
                      break;
                  }
                }
            }
      }
    
    DBG(2, ErrorF("setup is max X=%d max Y=%d resol X=%d resol Y=%d\n",
              common->wcmMaxX, common->wcmMaxY, common->wcmResolX,
              common->wcmResolY));
  
    /* send the tilt mode command after setup because it must be enabled */
    /* after multi-mode to take precedence */
    if (HANDLE_TILT(common)) {
      /* Unfortunately, the USB driver doesn't allow to send this
       * command to the tablet. Any other solutions ? */
      DBG(2, ErrorF("Sending tilt mode order\n"));
    }
  
    {
      if (common->wcmSuppress < 0) {
          common->wcmSuppress = 0;
      } else {
          if (common->wcmSuppress > 100) {
            common->wcmSuppress = 99;
          }
      }
      /* Cannot send WC_SUPPRESS to the table. Will have to do
       * this manually. */
    }
    
    priv->topX = 0;
    priv->bottomX = common->wcmMaxX;
    priv->topY = 0;
    priv->bottomY = common->wcmMaxY;

    if (xf86Verbose)
      ErrorF("%s Wacom tablet maximum X=%d maximum Y=%d "
             "X resolution=%d Y resolution=%d suppress=%d%s\n",
             XCONFIG_PROBED, common->wcmMaxX, common->wcmMaxY,
             common->wcmResolX, common->wcmResolY, common->wcmSuppress,
             HANDLE_TILT(common) ? " Tilt" : "");
  
    if (err < 0) {
        ErrorF("ERROR: %d\n", err);
      SYSCALL(close(local->fd));
      return !Success;
    }

    /* to have the button field handled as a bit field */
    common->wcmProtocolLevel = 5;
    
    return Success;
}

#endif /* LINUX_INPUT */

/*
 ***************************************************************************
 *
 * xf86WcmControlProc --
 *
 ***************************************************************************
 */
static void
xf86WcmControlProc(DeviceIntPtr     device,
               PtrCtrl  *ctrl)
{
  DBG(2, ErrorF("xf86WcmControlProc\n"));
}

/*
 ***************************************************************************
 *
 * xf86WcmOpen --
 *
 ***************************************************************************
 */
#define WAIT(t)                                       \
    err = xf86WaitForInput(-1, ((t) * 1000));               \
    if (err == -1) {                                  \
      ErrorF("Wacom select error : %s\n", strerror(errno)); \
      return !Success;                          \
    }

static Bool
xf86WcmOpen(LocalDevicePtr    local)
{
    char          buffer[256];
    char          header[64]; /* This is a small buffer for discarding the unwanted header */
    int                 err;
    WacomDevicePtr      priv = (WacomDevicePtr)local->private;
    WacomCommonPtr      common = priv->common;
    int                 a, b;
    int                 loop, idx;
    float         version = 0.0;
    int                 is_a_penpartner = 0;
    
    DBG(1, ErrorF("opening %s\n", common->wcmDevice));

    local->fd = xf86OpenSerial(local->options);
    if (local->fd < 0) {
      ErrorF("Error opening %s : %s\n", common->wcmDevice, strerror(errno));
      return !Success;
    }

#ifdef LINUX_INPUT
    DBG(1, ErrorF("testing USB\n"));
    
    if (xf86WcmIsUSBLine(local->fd)) {
      int   loop;
      
      SYSCALL(close(local->fd));

      for(loop=0; loop<common->wcmNumDevices; loop++) {
          common->wcmDevices[loop]->read_input=xf86WcmReadUSBInput;
      }
      common->wcmOpen=xf86WcmUSBOpen;
      
      return xf86WcmUSBOpen(local);
    }
#endif
    
    DBG(1, ErrorF("initializing tablet\n"));    

    /* Set the speed of the serial link to 38400 */
   if (xf86SetSerialSpeed(local->fd, 38400) < 0) {
       return !Success;
   }
    
    /* Send reset to the tablet */
    SYSCALL(err = write(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD)));
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }
    
    /* Wait 250 mSecs */
    WAIT(250);

    /* Send reset to the tablet */
    SYSCALL(err = write(local->fd, WC_RESET, strlen(WC_RESET)));
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }
    
    /* Wait 75 mSecs */
    WAIT(75);

    /* Set the speed of the serial link to 19200 */
   if (xf86SetSerialSpeed(local->fd, 19200) < 0) {
       return !Success;
   }
    
    /* Send reset to the tablet */
    SYSCALL(err = write(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD)));
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }
    
    /* Wait 250 mSecs */
    WAIT(250);

    /* Send reset to the tablet */
    SYSCALL(err = write(local->fd, WC_RESET, strlen(WC_RESET)));
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }
    
    /* Wait 75 mSecs */
    WAIT(75);

    /* Set the speed of the serial link to 9600 */
   if (xf86SetSerialSpeed(local->fd, 9600) < 0) {
       return !Success;
   }
    
    /* Send reset to the tablet */
    SYSCALL(err = write(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD)));
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }
    
    /* Wait 250 mSecs */
    WAIT(250);

    SYSCALL(err = write(local->fd, WC_STOP, strlen(WC_STOP)));
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }

    /* Wait 30 mSecs */
    WAIT(30);

    xf86FlushInput(local->fd);

    DBG(2, ErrorF("reading model\n"));
    if (!send_request(local->fd, WC_MODEL, buffer)) {
      return !Success;
    }
    DBG(2, ErrorF("%s\n", buffer));
  
    if (xf86Verbose) {
      ErrorF("%s Wacom tablet model : %s\n", XCONFIG_PROBED, buffer+2);
    }
    
    /* Answer is in the form ~#Tablet-Model VRom_Version */
    /* look for the first V from the end of the string */
    /* this seems to be the better way to find the version of the ROM */
    for(loop=strlen(buffer); loop>=0 && *(buffer+loop) != 'V'; loop--);
    for(idx=loop; idx<strlen(buffer) && *(buffer+idx) != '-'; idx++);
    *(buffer+idx) = '\0';

    /* Extract version numbers */
    sscanf(buffer+loop+1, "%f", &version);

    if ((buffer[2] == 'G' || buffer[2] == 'X') && buffer[3] == 'D') {
      DBG(2, ErrorF("detected an Intuos model\n"));
      common->wcmProtocolLevel = 5;
      common->wcmMaxZ = 1023;       /* max Z value */
      common->wcmResolX = 2540;     /* X resolution in points/inch */
      common->wcmResolY = 2540;     /* Y resolution in points/inch */
      common->wcmResolZ = 2540;     /* Z resolution in points/inch */
      common->wcmPktLength = 9;     /* length of a packet */
      if (common->wcmThreshold == INVALID_THRESHOLD) {
          common->wcmThreshold = -480; /* Threshold for counting pressure as a button */
          if (xf86Verbose) {
            ErrorF("%s Wacom using pressure threshold of %d for button 1\n",
                   XCONFIG_PROBED, common->wcmThreshold);
          }
      }
    }
      
    /* Tilt works on ROM 1.4 and above */
    DBG(2, ErrorF("wacom flags=%d ROM version=%f buffer=%s\n",
              common->wcmFlags, version, buffer+loop+1));
    if (common->wcmProtocolLevel == 4 &&
      (common->wcmFlags & TILT_FLAG) && (version >= (float)1.4)) {
      common->wcmPktLength = 9;
    }

    /* Check for a PenPartner or Graphire model which doesn't answer WC_CONFIG
     * request. The Graphire model is handled like a PenPartner except that
     * it doesn't answer WC_COORD requests.
     */
    if ((buffer[2] == 'C' || buffer[2] == 'E') && buffer[3] == 'T') {
      if (buffer[2] == 'E') {
          DBG(2, ErrorF("detected a Graphire model\n"));
          common->wcmFlags |= GRAPHIRE_FLAG;
          /* Graphire models don't answer WC_COORD requests */
          common->wcmMaxX = 5103;
          common->wcmMaxY = 3711;
          common->wcmMaxZ = 512;
      }
      else {
          DBG(2, ErrorF("detected a PenPartner model\n"));
          common->wcmMaxZ = 256;
      }
      common->wcmResolX = 1000;
      common->wcmResolY = 1000;
      is_a_penpartner = 1;
    }
    else if (common->wcmProtocolLevel == 4 && !(common->wcmResolX && common->wcmResolY)) {
      DBG(2, ErrorF("reading config\n"));
      if (send_request(local->fd, WC_CONFIG, buffer)) {
          DBG(2, ErrorF("%s\n", buffer));
          /* The header string is simply a place to put the unwanted
           * config header don't use buffer+xx because the header size
           * varies on different tablets
           */
          if (sscanf(buffer, "%[^,],%d,%d,%d,%d", header, &a, &b, &common->wcmResolX, &common->wcmResolY) == 5) {
            DBG(6, ErrorF("WC_CONFIG Header = %s\n", header));
          }
          else {
            ErrorF("WACOM: unable to parse resolution. Using default.\n");
            common->wcmResolX = common->wcmResolY = 1270;
          }
      }
      else {
          ErrorF("WACOM: unable to read resolution. Using default.\n");
          common->wcmResolX = common->wcmResolY = 1270;
      }
    }

    if (!(common->wcmFlags & GRAPHIRE_FLAG) && !(common->wcmMaxX && common->wcmMaxY)) {
      DBG(2, ErrorF("reading max coordinates\n"));
      if (!send_request(local->fd, WC_COORD, buffer)) {
          ErrorF("WACOM: unable to read max coordinates. Use the MaxX and MaxY options.\n");
          return !Success;
      }
      DBG(2, ErrorF("%s\n", buffer));
      if (sscanf(buffer+2, "%d,%d", &common->wcmMaxX, &common->wcmMaxY) != 2) {
          ErrorF("WACOM: unable to parse max coordinates. Use the MaxX and MaxY options.\n");
          return !Success;
      }
    }
    
    DBG(2, ErrorF("setup is max X=%d max Y=%d resol X=%d resol Y=%d\n",
              common->wcmMaxX, common->wcmMaxY, common->wcmResolX,
              common->wcmResolY));

    /* We can't change the resolution on PenPartner and Graphire models */
    if (!is_a_penpartner && common->wcmProtocolLevel == 4) {
      int resolX = common->wcmResolX, resolY = common->wcmResolY;
      
      /* Force the resolution.
       */
        if (((float)version) >= 1.2) {
          resolX = resolY = 2540;
      }
      sprintf(buffer, "%s%d\r", WC_NEW_RESOLUTION, resolX);
      SYSCALL(err = write(local->fd, buffer, strlen(buffer)));
      
      /* Verify the resolution change.
       */
      DBG(2, ErrorF("rereading config\n"));
      if (send_request(local->fd, WC_CONFIG, buffer)) {
          DBG(2, ErrorF("%s\n", buffer));
          /* The header string is simply a place to put the unwanted
           * config header don't use buffer+xx because the header size
           * varies on different tablets
           */
          if (sscanf(buffer, "%[^,],%d,%d,%d,%d", header, &a, &b, &common->wcmResolX, &common->wcmResolY) == 5) {
            DBG(6, ErrorF("WC_CONFIG Header = %s\n", header));
          }
          else {
            ErrorF("WACOM: unable to reparse resolution. Using previous values.\n");
          }
      }
      else {
          ErrorF("WACOM: unable to reread resolution. Using previous values.\n");
      }
      
      /* The following couple of lines convert the MaxX and MaxY returned by
       * the Wacom from 1270lpi to the Wacom's active resolution.
       */
      common->wcmMaxX = (common->wcmMaxX / MAX_COORD_RES) * common->wcmResolX;
      common->wcmMaxY = (common->wcmMaxY / MAX_COORD_RES) * common->wcmResolY;
    }
    
    DBG(2, ErrorF("setup is max X=%d max Y=%d resol X=%d resol Y=%d\n",
              common->wcmMaxX, common->wcmMaxY, common->wcmResolX,
              common->wcmResolY));
  
    /* Send a setup string to the tablet */
    if (is_a_penpartner) {
      SYSCALL(err = write(local->fd, penpartner_setup_string,
                      strlen(penpartner_setup_string)));
    }
    else if (common->wcmProtocolLevel == 4) {
        SYSCALL(err = write(local->fd, WC_RESET, strlen(WC_RESET)));
      WAIT(75);
      SYSCALL(err = write(local->fd, setup_string, strlen(setup_string)));
    }
    else {
      SYSCALL(err = write(local->fd, intuos_setup_string,
                      strlen(intuos_setup_string)));
    }
    
    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }

    /* Send the tilt mode command after setup because it must be enabled */
    /* after multi-mode to take precedence */
    if (common->wcmProtocolLevel == 4 && HANDLE_TILT(common)) {
      DBG(2, ErrorF("Sending tilt mode order\n"));
      
      SYSCALL(err = write(local->fd, WC_TILT_MODE, strlen(WC_TILT_MODE)));
      if (err == -1) {
          ErrorF("Wacom write error : %s\n", strerror(errno));
          return !Success;
      }
    }
  
    if (common->wcmSuppress < 0) {
      int   xratio = common->wcmMaxX/screenInfo.screens[0]->width;
      int   yratio = common->wcmMaxY/screenInfo.screens[0]->height;
      
      common->wcmSuppress = (xratio > yratio) ? yratio : xratio;
    }
    
    if (common->wcmSuppress > 100) {
      common->wcmSuppress = 99;
    }

    if (common->wcmProtocolLevel == 4) {
      char  buf[20];
      
      sprintf(buf, "%s%d\r", WC_SUPPRESS, common->wcmSuppress);
      SYSCALL(err = write(local->fd, buf, strlen(buf)));

      if (err == -1) {
          ErrorF("Wacom write error : %s\n", strerror(errno));
          return !Success;
      }
    }
    
    if (xf86Verbose)
      ErrorF("%s Wacom %s tablet maximum X=%d maximum Y=%d "
             "X resolution=%d Y resolution=%d suppress=%d%s\n",
             XCONFIG_PROBED, common->wcmProtocolLevel == 4 ? "IV" : "V",
             common->wcmMaxX, common->wcmMaxY,
             common->wcmResolX, common->wcmResolY, common->wcmSuppress,
             HANDLE_TILT(common) ? " Tilt" : "");
  
    if (err <= 0) {
      SYSCALL(close(local->fd));
      return !Success;
    }

    /* change the serial speed if requested */
    if (common->wcmLinkSpeed > 9600) {
      if (common->wcmProtocolLevel == 5) {
          char    *speed_init_string = WC_V_19200;
          DBG(1, ErrorF("Switching serial link to %d\n", common->wcmLinkSpeed));

          if (common->wcmLinkSpeed == 38400 && version < 2.0) {
            ErrorF("Wacom: 38400 speed not supported with this Intuos firmware (%f)\n", version);
            ErrorF("Switching to 19200\n");
            common->wcmLinkSpeed = 19200;
          }

          switch (common->wcmLinkSpeed) {
          case 38400:
            speed_init_string = WC_V_38400;
            break;

          case 19200:
            speed_init_string = WC_V_19200;
            break;
          }
          /* Switch the tablet to the requested speed */
          SYSCALL(err = write(local->fd, speed_init_string, strlen(speed_init_string)));
          if (err == -1) {
            ErrorF("Wacom write error : %s\n", strerror(errno));
            return !Success;
          }
          
          /* Wait 75 mSecs */
          WAIT(75);
    
          /* Set the speed of the serial link to requested speed */
          if (xf86SetSerialSpeed(local->fd, common->wcmLinkSpeed) < 0) {
            return !Success;
          }
      }
      else {
          ErrorF("Changing the speed of a wacom IV device is not yet implemented\n");
      }
    }

    /* Tell the tablet to start sending coordinates */
    SYSCALL(err = write(local->fd, WC_START, strlen(WC_START)));

    if (err == -1) {
      ErrorF("Wacom write error : %s\n", strerror(errno));
      return !Success;
    }

    return Success;
}

/*
 ***************************************************************************
 *
 * xf86WcmOpenDevice --
 *    Open the physical device and init information structs.
 *
 ***************************************************************************
 */
static int
xf86WcmOpenDevice(DeviceIntPtr       pWcm)
{
    LocalDevicePtr      local = (LocalDevicePtr)pWcm->public.devicePrivate;
    WacomDevicePtr      priv = (WacomDevicePtr)PRIVATE(pWcm);
    WacomCommonPtr      common = priv->common;
    double        screenRatio, tabletRatio;
    int                 gap;
    int                 loop;
    int                 screen_idx = 0;
    
    if (local->fd < 0) {
        if (common->wcmInitNumber > 2 ||
          priv->initNumber == common->wcmInitNumber) {
          if (common->wcmOpen(local) != Success) {
              if (local->fd >= 0) {
                SYSCALL(close(local->fd));
              }
              local->fd = -1;
          }
          else {
              /* report the file descriptor to all devices */
              for(loop=0; loop<common->wcmNumDevices; loop++) {
                common->wcmDevices[loop]->fd = local->fd;
              }
          }
          common->wcmInitNumber++;
          priv->initNumber = common->wcmInitNumber;
      }
      else {
        priv->initNumber = common->wcmInitNumber;
      }
    }

    if (local->fd != -1 &&
      priv->factorX == 0.0) {
      
      if (priv->bottomX == 0) priv->bottomX = common->wcmMaxX;

      if (priv->bottomY == 0) priv->bottomY = common->wcmMaxY;

      /* Verify Box validity */

      if (priv->topX > common->wcmMaxX ||
          priv->topX < 0) {
          ErrorF("Wacom invalid TopX (%d) reseting to 0\n", priv->topX);
          priv->topX = 0;
      }

      if (priv->topY > common->wcmMaxY ||
          priv->topY < 0) {
          ErrorF("Wacom invalid TopY (%d) reseting to 0\n", priv->topY);
          priv->topY = 0;
      }

      if (priv->bottomX > common->wcmMaxX ||
          priv->bottomX < priv->topX) {
          ErrorF("Wacom invalid BottomX (%d) reseting to %d\n",
               priv->bottomX, common->wcmMaxX);
          priv->bottomX = common->wcmMaxX;
      }

      if (priv->bottomY > common->wcmMaxY ||
          priv->bottomY < priv->topY) {
          ErrorF("Wacom invalid BottomY (%d) reseting to %d\n",
               priv->bottomY, common->wcmMaxY);
          priv->bottomY = common->wcmMaxY;
      }

      if (priv->screen_no != -1 &&
          (priv->screen_no >= screenInfo.numScreens ||
           priv->screen_no < 0)) {
          ErrorF("%s: invalid screen number %d, resetting to 0\n",
               local->name, priv->screen_no);
          priv->screen_no = 0;
      }

      /* Calculate the ratio according to KeepShape, TopX and TopY */

      if (priv->screen_no != -1) {
          screen_idx = priv->screen_no;
      }
      
      if (priv->flags & KEEP_SHAPE_FLAG) {
          screenRatio = ((double) screenInfo.screens[screen_idx]->width)
            / screenInfo.screens[screen_idx]->height;

          tabletRatio = ((double) (common->wcmMaxX - priv->topX))
            / (common->wcmMaxY - priv->topY);

          DBG(2, ErrorF("screenRatio = %.3g, tabletRatio = %.3g\n",
                    screenRatio, tabletRatio));

          if (screenRatio > tabletRatio) {
            gap = common->wcmMaxY * (1 - tabletRatio/screenRatio);
            priv->bottomX = common->wcmMaxX;
            priv->bottomY = common->wcmMaxY - gap;
          } else {
            gap = common->wcmMaxX * (1 - screenRatio/tabletRatio);
            priv->bottomX = common->wcmMaxX - gap;
            priv->bottomY = common->wcmMaxY;
          }
      }
      priv->factorX = ((double) screenInfo.screens[0]->width)
          / (priv->bottomX - priv->topX);
      priv->factorY = ((double) screenInfo.screens[0]->height)
          / (priv->bottomY - priv->topY);
    
      if (xf86Verbose)
          ErrorF("%s Wacom tablet top X=%d top Y=%d "
               "bottom X=%d bottom Y=%d\n",
               XCONFIG_PROBED, priv->topX, priv->topY,
               priv->bottomX, priv->bottomY);
      
      DBG(2, ErrorF("X factor = %.3g, Y factor = %.3g\n",
                  priv->factorX, priv->factorY));
    }

    /* Check threshold correctness */
    DBG(2, ErrorF("Threshold=%d\n", common->wcmThreshold));
    
    if (common->wcmThreshold > common->wcmMaxZ ||
      common->wcmThreshold < 0) {
      if (((common->wcmProtocolLevel == 5) ||
           (common->wcmFlags & GRAPHIRE_FLAG)) &&
          xf86Verbose &&
          common->wcmThreshold != INVALID_THRESHOLD)
          ErrorF("%s Wacom invalid threshold %d. Reset to %d\n",
               XCONFIG_PROBED, common->wcmThreshold, common->wcmMaxZ / 3);
      common->wcmThreshold = common->wcmMaxZ / 3;
    }
    DBG(2, ErrorF("New threshold=%d\n", common->wcmThreshold));    

    /* Set the real values */
    InitValuatorAxisStruct(pWcm,
                     0,
                     0,       /* min val */
                     priv->bottomX - priv->topX, /* max val */
                     mils(common->wcmResolX), /* resolution */
                     0,       /* min_res */
                     mils(common->wcmResolX)); /* max_res */
    InitValuatorAxisStruct(pWcm,
                     1,
                     0,       /* min val */
                     priv->bottomY - priv->topY, /* max val */
                     mils(common->wcmResolY), /* resolution */
                     0,       /* min_res */
                     mils(common->wcmResolY)); /* max_res */
    InitValuatorAxisStruct(pWcm,
                     2,
                     0,       /* min val */
                     common->wcmMaxZ, /* max val */
                     mils(common->wcmResolZ), /* resolution */
                     0,       /* min_res */
                     mils(common->wcmResolZ)); /* max_res */
    InitValuatorAxisStruct(pWcm,
                     3,
                     -64,           /* min val */
                     63,            /* max val */
                     128,           /* resolution ??? */
                     0,
                     128);
    InitValuatorAxisStruct(pWcm,
                     4,
                     -64,           /* min val */
                     63,            /* max val */
                     128,           /* resolution ??? */
                     0,
                     128);
    InitValuatorAxisStruct(pWcm,
                     5,
                     0,       /* min val */
                     1023,        /* max val */
                     128,           /* resolution ??? */
                     0,
                     128);
    
    return (local->fd != -1);
}

/*
 ***************************************************************************
 *
 * xf86WcmClose --
 *
 ***************************************************************************
 */
static void
xf86WcmClose(LocalDevicePtr   local)
{
    WacomDevicePtr      priv = (WacomDevicePtr)local->private;
    WacomCommonPtr      common = priv->common;
    int                 loop;
    int                 num = 0;
    
    for(loop=0; loop<common->wcmNumDevices; loop++) {
      if (common->wcmDevices[loop]->fd >= 0) {
          num++;
      }
    }
    DBG(4, ErrorF("Wacom number of open devices = %d\n", num));
    
    if (num == 1) {               
      SYSCALL(close(local->fd));
    }
    
    local->fd = -1;
}

/*
 ***************************************************************************
 *
 * xf86WcmProc --
 *      Handle the initialization, etc. of a wacom
 *
 ***************************************************************************
 */
static int
xf86WcmProc(DeviceIntPtr       pWcm,
          int                what)
{
    CARD8                 map[(32 << 4) + 1];
    int                   nbaxes;
    int                   nbbuttons;
    int                   loop;
    LocalDevicePtr        local = (LocalDevicePtr)pWcm->public.devicePrivate;
    WacomDevicePtr        priv = (WacomDevicePtr)PRIVATE(pWcm);
  
    DBG(2, ErrorF("BEGIN xf86WcmProc dev=0x%x priv=0x%x type=%s flags=%d what=%d\n",
              pWcm, priv, (DEVICE_ID(priv->flags) == STYLUS_ID) ? "stylus" :
              (DEVICE_ID(priv->flags) == CURSOR_ID) ? "cursor" : "eraser",
              priv->flags, what));
  
    switch (what)
      {
      case DEVICE_INIT: 
          DBG(1, ErrorF("xf86WcmProc pWcm=0x%x what=INIT\n", pWcm));
      
          nbaxes = 6;         /* X, Y, Pressure, Tilt-X, Tilt-Y, Wheel */
          
          switch(DEVICE_ID(priv->flags)) {
          case ERASER_ID:
            nbbuttons = 1;
            break;
          case STYLUS_ID:
            nbbuttons = 4;
            break;
          default:
            nbbuttons = 16;
            break;
          }
          
          for(loop=1; loop<=nbbuttons; loop++) map[loop] = loop;

          if (InitButtonClassDeviceStruct(pWcm,
                                  nbbuttons,
                                  map) == FALSE) {
            ErrorF("unable to allocate Button class device\n");
            return !Success;
          }
      
          if (InitFocusClassDeviceStruct(pWcm) == FALSE) {
            ErrorF("unable to init Focus class device\n");
            return !Success;
          }
          
          if (InitPtrFeedbackClassDeviceStruct(pWcm,
                                     xf86WcmControlProc) == FALSE) {
            ErrorF("unable to init ptr feedback\n");
            return !Success;
          }
          
          if (InitProximityClassDeviceStruct(pWcm) == FALSE) {
            ErrorF("unable to init proximity class device\n");
            return !Success;
          }

          if (InitKeyClassDeviceStruct(pWcm, &wacom_keysyms, NULL) == FALSE) {
            ErrorF("unable to init key class device\n"); 
            return !Success;
          }

          if (InitValuatorClassDeviceStruct(pWcm, 
                                    nbaxes,
                                    xf86GetMotionEvents, 
                                    local->history_size,
                                    ((priv->flags & ABSOLUTE_FLAG) 
                                    ? Absolute : Relative) |
                                    OutOfProximity)
            == FALSE) {
            ErrorF("unable to allocate Valuator class device\n"); 
            return !Success;
          }
          else {
            /* allocate the motion history buffer if needed */
            xf86MotionHistoryAllocate(local);
          }

          /* open the device to gather informations */
          xf86WcmOpenDevice(pWcm);

          break; 
      
      case DEVICE_ON:
          DBG(1, ErrorF("xf86WcmProc pWcm=0x%x what=ON\n", pWcm));

          if ((local->fd < 0) && (!xf86WcmOpenDevice(pWcm))) {
            return !Success;
          }
          xf86AddEnabledDevice(local);
          pWcm->public.on = TRUE;
          break;
      
      case DEVICE_OFF:
          DBG(1, ErrorF("xf86WcmProc  pWcm=0x%x what=%s\n", pWcm,
                    (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
          if (local->fd >= 0) {
            xf86RemoveEnabledDevice(local);
            xf86WcmClose(local);
          }
          pWcm->public.on = FALSE;
          break;
      
      case DEVICE_CLOSE:
          DBG(1, ErrorF("xf86WcmProc  pWcm=0x%x what=%s\n", pWcm,
                    (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
          xf86WcmClose(local);
          break;

      default:
          ErrorF("unsupported mode=%d\n", what);
          return !Success;
          break;
      }
    DBG(2, ErrorF("END   xf86WcmProc Success what=%d dev=0x%x priv=0x%x\n",
              what, pWcm, priv));
    return Success;
}

/*
 ***************************************************************************
 *
 * xf86WcmChangeControl --
 *
 ***************************************************************************
 */
static int
xf86WcmChangeControl(LocalDevicePtr local,
                 xDeviceCtl         *control)
{
    xDeviceResolutionCtl      *res;
    int                       *resolutions;
    char                str[10];
  
    res = (xDeviceResolutionCtl *)control;
      
    if ((control->control != DEVICE_RESOLUTION) ||
      (res->num_valuators < 1))
      return (BadMatch);
  
    resolutions = (int *)(res +1);
    
    DBG(3, ErrorF("xf86WcmChangeControl changing to %d (suppressing under)\n",
              resolutions[0]));

    sprintf(str, "SU%d\r", resolutions[0]);
    SYSCALL(write(local->fd, str, strlen(str)));
  
    return(Success);
}

/*
 ***************************************************************************
 *
 * xf86WcmSwitchMode --
 *
 ***************************************************************************
 */
static int
xf86WcmSwitchMode(ClientPtr   client,
              DeviceIntPtr    dev,
              int       mode)
{
    LocalDevicePtr        local = (LocalDevicePtr)dev->public.devicePrivate;
    WacomDevicePtr        priv = (WacomDevicePtr)local->private;

    DBG(3, ErrorF("xf86WcmSwitchMode dev=0x%x mode=%d\n", dev, mode));
  
    if (mode == Absolute) {
      priv->flags = priv->flags | ABSOLUTE_FLAG;
    }
    else {
      if (mode == Relative) {
          priv->flags = priv->flags & ~ABSOLUTE_FLAG; 
      }
      else {
          DBG(1, ErrorF("xf86WcmSwitchMode dev=0x%x invalid mode=%d\n", dev,
                    mode));
          return BadMatch;
      }
    }
    return Success;
}

/*
 ***************************************************************************
 *
 * xf86WcmAllocate --
 *
 ***************************************************************************
 */
static LocalDevicePtr
xf86WcmAllocate(char *  name,
                int     flag)
{
    LocalDevicePtr        local;
    WacomDevicePtr        priv;
    WacomCommonPtr        common;

    priv = (WacomDevicePtr) xalloc(sizeof(WacomDeviceRec));
    if (!priv)
      return NULL;

    common = (WacomCommonPtr) xalloc(sizeof(WacomCommonRec));
    if (!common) {
      xfree(priv);
      return NULL;
    }

    local = xf86AllocateInput(wcmDrv, 0);
    if (!local) {
      xfree(priv);
      xfree(common);
      return NULL;
    }

    local->name = name;
    local->flags = 0;
    local->device_control = xf86WcmProc;
    local->read_input = xf86WcmReadInput;
    local->control_proc = xf86WcmChangeControl;
    local->close_proc = xf86WcmClose;
    local->switch_mode = xf86WcmSwitchMode;
    local->conversion_proc = xf86WcmConvert;
    local->reverse_conversion_proc = xf86WcmReverseConvert;
    local->fd = -1;
    local->atom = 0;
    local->dev = NULL;
    local->private = priv;
    local->private_flags = 0;
    local->history_size  = 0;
    local->old_x = -1;
    local->old_y = -1;
    
    priv->flags = flag;             /* various flags (device type, absolute, first touch...) */
    priv->oldX = -1;                /* previous X position */
    priv->oldY = -1;                /* previous Y position */
    priv->oldZ = -1;                /* previous pressure */
    priv->oldTiltX = -1;            /* previous tilt in x direction */
    priv->oldTiltY = -1;            /* previous tilt in y direction */
    priv->oldButtons = 0;           /* previous buttons state */
    priv->oldProximity = 0;         /* previous proximity */
    priv->topX = 0;                 /* X top */
    priv->topY = 0;                 /* Y top */
    priv->bottomX = 0;              /* X bottom */
    priv->bottomY = 0;              /* Y bottom */
    priv->factorX = 0.0;            /* X factor */
    priv->factorY = 0.0;            /* Y factor */
    priv->common = common;          /* common info pointer */
    priv->oldProximity = 0;         /* previous proximity */
    priv->serial = 0;                 /* serial number */
    priv->initNumber = 0;             /* magic number for the init phasis */
    priv->screen_no = -1;           /* associated screen */
    
    common->wcmDevice = "";         /* device file name */
    common->wcmSuppress = -1;       /* transmit position if increment is superior */
    common->wcmFlags = 0;           /* various flags */
    common->wcmDevices = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr));
    common->wcmDevices[0] = local;
    common->wcmNumDevices = 1;            /* number of devices */
    common->wcmIndex = 0;           /* number of bytes read */
    common->wcmPktLength = 7;       /* length of a packet */
    common->wcmMaxX = 0;            /* max X value */
    common->wcmMaxY = 0;            /* max Y value */
    common->wcmMaxZ = DEFAULT_MAXZ; /* max Z value */
    common->wcmResolX = 0;          /* X resolution in points/inch */
    common->wcmResolY = 0;          /* Y resolution in points/inch */
    common->wcmResolZ = 1270;       /* Z resolution in points/inch */
    common->wcmHasEraser = (flag & ERASER_ID) ? TRUE : FALSE;     /* True if an eraser has been configured */
    common->wcmStylusSide = TRUE;   /* eraser or stylus ? */
    common->wcmStylusProximity = FALSE;   /* a stylus is in proximity ? */
    common->wcmProtocolLevel = 4;   /* protocol level */
    common->wcmThreshold = INVALID_THRESHOLD; /* button 1 threshold for some tablet models */
    common->wcmInitNumber = 0;              /* magic number for the init phasis */
    common->wcmLinkSpeed = 9600;        /* serial link speed */
    common->wcmOpen = xf86WcmOpen;  /* function used to open the line (serial or USB) */
    common->wcmLastSerial = 0;            /* last device (used by the USB part) */
    return local;
}

/*
 ***************************************************************************
 *
 * xf86WcmAllocateStylus --
 *
 ***************************************************************************
 */
static LocalDevicePtr
xf86WcmAllocateStylus()
{
    LocalDevicePtr        local = xf86WcmAllocate(XI_STYLUS, STYLUS_ID);

    if (local)
      local->type_name = "Wacom Stylus";
    return local;
}

/*
 ***************************************************************************
 *
 * xf86WcmAllocateCursor --
 *
 ***************************************************************************
 */
static LocalDevicePtr
xf86WcmAllocateCursor()
{
    LocalDevicePtr        local = xf86WcmAllocate(XI_CURSOR, CURSOR_ID);

    if (local)
      local->type_name = "Wacom Cursor";
    return local;
}

/*
 ***************************************************************************
 *
 * xf86WcmAllocateEraser --
 *
 ***************************************************************************
 */
static LocalDevicePtr
xf86WcmAllocateEraser()
{
    LocalDevicePtr        local = xf86WcmAllocate(XI_ERASER, ABSOLUTE_FLAG|ERASER_ID);

    if (local)
      local->type_name = "Wacom Eraser";
    return local;
}

/*
 ***************************************************************************
 *
 * Wacom Stylus device association --
 *
 ***************************************************************************
 */
DeviceAssocRec wacom_stylus_assoc =
{
    STYLUS_SECTION_NAME,            /* config_section_name */
    xf86WcmAllocateStylus           /* device_allocate */
};

/*
 ***************************************************************************
 *
 * Wacom Cursor device association --
 *
 ***************************************************************************
 */
DeviceAssocRec wacom_cursor_assoc =
{
    CURSOR_SECTION_NAME,            /* config_section_name */
    xf86WcmAllocateCursor           /* device_allocate */
};

/*
 ***************************************************************************
 *
 * Wacom Eraser device association --
 *
 ***************************************************************************
 */
DeviceAssocRec wacom_eraser_assoc =
{
    ERASER_SECTION_NAME,            /* config_section_name */
    xf86WcmAllocateEraser           /* device_allocate */
};


/*
 * xf86WcmUninit --
 *
 * called when the driver is unloaded.
 */
static void
xf86WcmUninit(InputDriverPtr  drv,
            LocalDevicePtr    local,
            int flags)
{
    WacomDevicePtr      priv = (WacomDevicePtr) local->private;
    
    DBG(1, ErrorF("xf86WcmUninit\n"));
    
    xf86WcmProc(local->dev, DEVICE_OFF);
    
    xfree (priv);
    xf86DeleteInput(local, 0);    
}

/*
 * xf86WcmInit --
 *
 * called when the module subsection is found in XF86Config
 */
static InputInfoPtr
xf86WcmInit(InputDriverPtr    drv,
          IDevPtr       dev,
          int                 flags)
{
    LocalDevicePtr      local = NULL;
    LocalDevicePtr      fakeLocal = NULL;
    WacomDevicePtr      priv = NULL;
    WacomCommonPtr      common = NULL;
    char          *s;
    LocalDevicePtr      localDevices;

    wcmDrv = drv;

    fakeLocal = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec));
    if (!fakeLocal)
      return NULL;

    fakeLocal->conf_idev = dev;

    /* Force default serial port options to exist because the serial init
     * phasis is based on those values.
     */
    xf86CollectInputOptions(fakeLocal, default_options, NULL);

    /* Type is mandatory */
    s = xf86FindOptionValue(fakeLocal->options, "Type");

    if (s && (xf86NameCmp(s, "stylus") == 0)) {
      local = xf86WcmAllocateStylus();
    }
    else if (s && (xf86NameCmp(s, "cursor") == 0)) {
      local = xf86WcmAllocateCursor();
    }
    else if (s && (xf86NameCmp(s, "eraser") == 0)) {
      local = xf86WcmAllocateEraser();
    }
    else {
      xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
            "Must be one of stylus, cursor or eraser\n",
            dev->identifier);
      goto SetupProc_fail;
    }
    
    if (!local) {
      xfree(fakeLocal);
      return NULL;
    }

    priv = (WacomDevicePtr) local->private;
    common = priv->common;

    local->options = fakeLocal->options;
    local->conf_idev = fakeLocal->conf_idev;
    local->name = dev->identifier;
    xfree(fakeLocal);
    
    /* Serial Device is mandatory */
    common->wcmDevice = xf86FindOptionValue(local->options, "Device");

    if (!common->wcmDevice) {
      xf86Msg (X_ERROR, "%s: No Device specified.\n", dev->identifier);
      goto SetupProc_fail;
    }

    /* Lookup to see if there is another wacom device sharing
     * the same serial line.
     */
    localDevices = xf86FirstLocalDevice();
    
    while(localDevices) {
      if ((local != localDevices) &&
          (localDevices->device_control == xf86WcmProc) &&
          (strcmp(((WacomDevicePtr)localDevices->private)->common->wcmDevice,
                common->wcmDevice) == 0)) {
            DBG(2, ErrorF("xf86WcmConfig wacom port share between"
                        " %s and %s\n",
                        local->name, localDevices->name));
            ((WacomDevicePtr) localDevices->private)->common->wcmHasEraser |= common->wcmHasEraser;
            xfree(common->wcmDevices);
            xfree(common);
            common = priv->common = ((WacomDevicePtr) localDevices->private)->common;
            common->wcmNumDevices++;
            common->wcmDevices = (LocalDevicePtr *) xrealloc(common->wcmDevices,
                                                 sizeof(LocalDevicePtr) * common->wcmNumDevices);
            common->wcmDevices[common->wcmNumDevices - 1] = local;
            break;
      }
      localDevices = localDevices->next;
    }

    /* Process the common options. */
    xf86ProcessCommonOptions(local, local->options);

    /* Optional configuration */

    xf86Msg(X_CONFIG, "%s serial device is %s\n", dev->identifier,
          common->wcmDevice);

    debug_level = xf86SetIntOption(local->options, "DebugLevel", debug_level);
    if (debug_level > 0) {
      xf86Msg(X_CONFIG, "WACOM: debug level set to %d\n", debug_level);
    }

    s = xf86FindOptionValue(local->options, "Mode");

    if (s && (xf86NameCmp(s, "absolute") == 0)) {
      priv->flags = priv->flags | ABSOLUTE_FLAG;
    }
    else if (s && (xf86NameCmp(s, "relative") == 0)) {
      priv->flags = priv->flags & ~ABSOLUTE_FLAG;
    }
    else if (s) {
      xf86Msg(X_ERROR, "%s: invalid Mode (should be absolute or relative). Using default.\n",
            dev->identifier);
    }
    xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name,
          (priv->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");         

    common->wcmSuppress = xf86SetIntOption(local->options, "Suppress", common->wcmSuppress);
    if (common->wcmSuppress != -1) {
      xf86Msg(X_CONFIG, "WACOM: suppress value is %d\n", XCONFIG_GIVEN,
            common->wcmSuppress);      
    }
    
    if (xf86SetBoolOption(local->options, "Tilt", (common->wcmFlags & TILT_FLAG))) {
      common->wcmFlags |= TILT_FLAG;
    }

#ifdef LINUX_INPUT
    if (xf86SetBoolOption(local->options, "USB", (common->wcmOpen == xf86WcmUSBOpen))) {
      local->read_input=xf86WcmReadUSBInput;
      common->wcmOpen=xf86WcmUSBOpen;
      xf86Msg(X_CONFIG, "%s: reading USB link\n", dev->identifier);
#else
    if (xf86SetBoolOption(local->options, "USB", 0)) {
      ErrorF("The USB version of the driver isn't available for your platform\n");
#endif
    }

    priv->screen_no = xf86SetIntOption(local->options, "ScreenNo", -1);
    if (priv->screen_no != -1) {
      xf86Msg(X_CONFIG, "%s: attached screen number %d\n", dev->identifier,
            priv->screen_no);
    }
      
    if (xf86SetBoolOption(local->options, "KeepShape", 0)) {
      priv->flags |= KEEP_SHAPE_FLAG;
      xf86Msg(X_CONFIG, "%s: keeps shape\n", dev->identifier);
    }

    priv->topX = xf86SetIntOption(local->options, "TopX", 0);
    if (priv->topX != 0) {
      xf86Msg(X_CONFIG, "%s: top x = %d\n", dev->identifier, priv->topX);
    }
    priv->topY = xf86SetIntOption(local->options, "TopY", 0);
    if (priv->topY != 0) {
      xf86Msg(X_CONFIG, "%s: top x = %d\n", dev->identifier, priv->topY);
    }
    priv->bottomX = xf86SetIntOption(local->options, "BottomX", 0);
    if (priv->bottomX != 0) {
      xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier,
            priv->bottomX);
    }
    priv->bottomY = xf86SetIntOption(local->options, "BottomY", 0);
    if (priv->bottomY != 0) {
      xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier,
            priv->bottomY);
    }
    priv->serial = xf86SetIntOption(local->options, "Serial", 0);
    if (priv->bottomY != 0) {
      xf86Msg(X_CONFIG, "%s: serial number = %u\n", dev->identifier,
            priv->serial);
    }
    common->wcmThreshold = xf86SetIntOption(local->options, "Threshold", common->wcmThreshold);
    if (common->wcmThreshold != INVALID_THRESHOLD) {
      xf86Msg(X_CONFIG, "%s: threshold = %d\n", dev->identifier,
            common->wcmThreshold);
    }
    common->wcmMaxX = xf86SetIntOption(local->options, "MaxX", common->wcmMaxX);
    if (common->wcmMaxX != 0) {
      xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier,
            common->wcmMaxX);
    }
    common->wcmMaxY = xf86SetIntOption(local->options, "MaxY", common->wcmMaxY);
    if (common->wcmMaxY != 0) {
      xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier,
            common->wcmMaxY);
    }
    common->wcmMaxZ = xf86SetIntOption(local->options, "MaxZ", common->wcmMaxZ);
    if (common->wcmMaxZ != DEFAULT_MAXZ) {
      xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier,
            common->wcmMaxZ);
    }
    common->wcmResolX = xf86SetIntOption(local->options, "ResolutionX", common->wcmResolX);
    if (common->wcmResolX != 0) {
      xf86Msg(X_CONFIG, "%s: resol x = %d\n", dev->identifier,
            common->wcmResolX);
    }
    common->wcmResolY = xf86SetIntOption(local->options, "ResolutionY", common->wcmResolY);
    if (common->wcmResolY != 0) {
      xf86Msg(X_CONFIG, "%s: resol y = %d\n", dev->identifier,
            common->wcmResolY);
    }
    common->wcmResolZ = xf86SetIntOption(local->options, "ResolutionZ", common->wcmResolZ);
    if (common->wcmResolZ != 0) {
      xf86Msg(X_CONFIG, "%s: resol z = %d\n", dev->identifier,
            common->wcmResolZ);
    }

    {
      int   val;
      val = xf86SetIntOption(local->options, "BaudRate", 0);

      switch(val) {
      case 38400:
          common->wcmLinkSpeed = 38400;
          break;
      case 19200:
          common->wcmLinkSpeed = 19200;
          break;
      case 9600:
          common->wcmLinkSpeed = 9600;
          break;
      default:
          xf86Msg(X_ERROR, "%s: Illegal speed value (must be 9600 or 19200 or 38400).", dev->identifier);
          break;
      }
      if (xf86Verbose)
          xf86Msg(X_CONFIG, "%s: serial speed %u\n", dev->identifier,
                val);
    }
    /* mark the device configured */
    local->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;

    /* return the LocalDevice */
    return (local);

  SetupProc_fail:
    if (common)
      xfree(common);
    if (priv)
      xfree(priv);
    if (local)
      xfree(local);
    return NULL;
}

#ifdef XFree86LOADER
static
#endif
InputDriverRec WACOM = {
    1,                        /* driver version */
    "wacom",                  /* driver name */
    NULL,               /* identify */
    xf86WcmInit,        /* pre-init */
    xf86WcmUninit,            /* un-init */
    NULL,               /* module */
    0                   /* ref count */
};

/*
 ***************************************************************************
 *
 * Dynamic loading functions
 *
 ***************************************************************************
 */
#ifdef XFree86LOADER
/*
 * xf86WcmUnplug --
 *
 * called when the module subsection is found in XF86Config
 */
static void
xf86WcmUnplug(pointer   p)
{
    DBG(1, ErrorF("xf86WcmUnplug\n"));
}

/*
 * xf86WcmPlug --
 *
 * called when the module subsection is found in XF86Config
 */
static pointer
xf86WcmPlug(pointer     module,
          pointer options,
          int           *errmaj,
          int           *errmin)
{

    /* The following message causes xf86cfg to puke. Commented out for now */
#if 0
    xf86Msg(X_INFO, "Wacom driver level: %s\n", identification+strlen("$Identification: ")); 
#endif    

    xf86AddInputDriver(&WACOM, module, 0);

    return module;
}

static XF86ModuleVersionInfo xf86WcmVersionRec =
{
    "wacom",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XF86_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 */
};

XF86ModuleData wacomModuleData = {&xf86WcmVersionRec,
                          xf86WcmPlug,
                          xf86WcmUnplug};

#endif /* XFree86LOADER */

/*
 * Local variables:
 * change-log-default-name: "~/xinput.log"
 * c-file-style: "bsd"
 * End:
 */
/* end of xf86Wacom.c */

Generated by  Doxygen 1.6.0   Back to index