xf86Aiptek.c   [plain text]


/*
 * xf86Aiptek
 *
 * This driver assumes Linux USB/HID support, available for USB devices.
 * 
 * Version 0.0, 1-Jan-2003, Bryan W. Headley
 * 
 * Copyright 2003 by Bryan W. Headley. <bwheadley@earthlink.net>
 *
 * Lineage: This driver is based on both the xf86HyperPen and xf86Wacom tablet
 *          drivers.
 *
 *      xf86HyperPen -- modified from xf86Summa (c) 1996 Steven Lang
 *          (c) 2000 Roland Jansen
 *          (c) 2000 Christian Herzog (button & 19200 baud support)
 *
 *      xf86Wacom -- (c) 1995-2001 Frederic Lepied
 *
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Bryan W. Headley not be used in 
 * advertising or publicity pertaining to distribution of the software 
 * without specific, written prior permission.  Bryan W. Headley makes no 
 * representations about the suitability of this software for any purpose.  
 * It is provided "as is" without express or implied warranty.
 *
 * BRYAN W. HEADLEY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL BRYAN W. HEADLEY BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
/* $XFree86: xc/programs/Xserver/hw/xfree86/input/aiptek/xf86Aiptek.c,v 1.2 2003/12/22 17:48:10 tsi Exp $ */

/*
 *
 * Section "InputDevice"
 *      Identifier  "stylus"
 *      Driver      "aiptek"
 *      Option      "Device"        "pathname" {/dev/input/event0}
 *      Option      "Type"          "string" {stylus|cursor|eraser}
 *      Option      "Mode"          "string" {absolute|relative}
 *      Option      "Cursor"        "string" {stylus|puck}
 *      Option      "USB"           "bool"   {on|off}
 *      Option      "ScreenNo"      "int"
 *      Option      "KeepShape"     "bool"   {on|off}
 *
 *      # The tablet reports top-right as 0 for the given coordinate
 *      # to bottom-left as num (value dependent on tablet.)
 *      # If you choose to invert X, Y or both, the bottom-left coordinate
 *      # is reported as 0.
 *      Option      "InvX"          "bool"   {on|off}
 *      Option      "InvY"          "bool"   {on|off}
 *
 *      # XSize/YSize/XOffset/YOffset allow you to specify an active
 *      # area within yout tablet.
 *
 *      Option      "XSize"         "int"
 *      Option      "YSize"         "int"
 *      
 *      Option      "XTop"          "int"
 *      Option      "YTop"          "int"
 *      Option      "XBottom"       "int"
 *      Option      "YBottom"       "int"
 *      Option      "XOffset"       "int"
 *      Option      "YOffset"       "int"
 *
 *      Option      "XMax"          "int"
 *      Option      "YMax"          "int"
 *      Option      "ZMax"          "int"
 *      Option      "ZMin"          "int"
 *
 *      Option      "XThreshold"  "int"
 *      Option      "YThreshold"  "int"
 *      Option      "ZThreshold"  "int"
 *
 *      Option      "Pressure"      "Soft|Hard|Linear"  defaults to Linear
 *
 *      Option      "alwayscore"  "bool"   {on|off}
 *      Option      "debuglevel"  "int"
 *      Option      "HistorySize" "int"
 * EndSection
 *
 *  Commentary:
 *  1.  Identifier: what you name your input device is not too
 *      significant. 
 *      but what it infers is that you can have a driver with
 *      a name of "stylus" (whose type would be 'stylus') and
 *      another one with identifier "cursor" (whose type would be
 *      'cursor') that both would be driver by the same aiptek
 *      driver. Note though that the identifier keyword has
 *      no devicetype connotations: you can identify your input
 *      device as "zzz", 
 *
 */
#include "xf86Aiptek.h"

static const char identification[] = "$Identification: 0 $";
static InputDriverPtr aiptekDrv;
static int debug_level = INI_DEBUG_LEVEL;

#ifdef XFree86LOADER
static
#endif
InputDriverRec AIPTEK =
{
    1,                  /* driver version */
    "aiptek",           /* driver name */
    NULL,               /* identify */
    xf86AiptekInit,     /* pre-init */
    xf86AiptekUninit,   /* un-init */
    NULL,               /* module */
    0                   /* ref count */
};

/*
 * Function/Macro keys variables.
 *
 * This is a list of X keystrokes the macro keys can send.
 */
static KeySym aiptek_map[] =
{
    /* 0x00 .. 0x07 */
    NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,
    /* 0x08 .. 0x0f */
    XK_F1,  XK_F2,  XK_F3,  XK_F4,  XK_F5,  XK_F6,  XK_F7,  XK_F8,
    /* 0x10 .. 0x17 */
    XK_F9,  XK_F10, XK_F11, XK_F12, XK_F13, XK_F14, XK_F15, XK_F16,
    /* 0x18 .. 0x1f */
    XK_F17, XK_F18, XK_F19, XK_F20, XK_F21, XK_F22, XK_F23, XK_F24,
    /* 0x20 .. 0x27 */
    XK_F25, XK_F26, XK_F27, XK_F28, XK_F29, XK_F30, XK_F31, XK_F32
};

/*
 * This is the map of Linux Event Input system keystrokes sent for
 * the macro keys. There are discrepancies in the mappings, so for example,
 * if we wanted to implement full macro key-to-string conversion in the
 * Linux driver, we'd have to accept 1-to-many keyboard events, several of
 * whom would not have the same encoding. For this reason, we're biting
 * the bullet now & implementing a simple lookup/translation scheme.
 * A simple 'KEY_F1 = XK_F1' layout wouldn't work, because X wants an
 * offset into the KeySym array above, and it'll look up that this means 
 * XK_whatever...
 */

static int linux_inputDevice_keyMap[] =
{
    KEY_F1,  KEY_F2,  KEY_F3,  KEY_F4,  KEY_F5,  KEY_F6,  KEY_F7,  KEY_F8,
    KEY_F9,  KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16,
    KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24, 
    KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, KEY_FRONT, KEY_COPY,
    KEY_OPEN, KEY_PASTE
};

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

static const char *default_options[] =
{
    "BaudRate",     "9600",
    "StopBits",     "1",
    "DataBits",     "8",
    "Parity",       "None",
    "VMin",         "1",
    "Vtime",        "10",
    "FlowControl",  "Xoff",
    NULL
};

/*
 * xf86AiptekConvert
 * Convert valuators to X and Y. We deal with multiple X screens, adjusting
 * for xTop/xBottom/yTop/yBottom (or xSize/ySize).
 */
static Bool
xf86AiptekConvert(LocalDevicePtr local,
                  int            first,
                  int            num,
                  int            v0,
                  int            v1,
                  int            v2,
                  int            v3,
                  int            v4,
                  int            v5,
                  int*           x,
                  int*           y)
{
    AiptekDevicePtr device = (AiptekDevicePtr) local->private;
    int  xSize, ySize;
    int  width, height;

    DBG(6, ErrorF("xf86AiptekConvert\n"));
    xf86Msg(X_CONFIG, " xf86AiptekConvert(), with: first=%d, num=%d, v0=%d, "
            "v1=%d, v2=%d, v3=%d,, v4=%d, v5=%d, x=%d, y=%d\n",
            first, num, v0, v1, v2, v3, v4, v5, *x, *y);

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

    xSize = device->xBottom - device->xTop;
    ySize = device->yBottom - device->yTop;

    width  = screenInfo.screens[device->screenNo]->width;
    height = screenInfo.screens[device->screenNo]->height;

    *x = (v0 * width)  / xSize;
    *y = (v1 * height) / ySize;

    /* Deal with coordinate inversion */
    if ( device->flags & INVX_FLAG)
    {
        *x = width - *x;
    }
    if ( device->flags & INVY_FLAG)
    {
        *y = height - *y;
    }

    /* Normalize the adjusted sizes. */
    if (*x < 0)
    {
        *x = 0;
    }
    if (*x > width)
    {
        *x = width;
    }

    if (*y < 0)
    {
        *y = 0;
    }
    if (*y > height)
    {
        *y = height;
    }

    if (device->screenNo != 0)
    {
        xf86XInputSetScreen(local, device->screenNo, *x, *y);
    }
    xf86Msg(X_CONFIG, ": xf86AiptekConvert() exits, with: x=%d, y=%d\n",
            *x, *y);

    return TRUE;
}

/*
 * xf86AiptekReverseConvert
 * Convert X and Y to valuators.
 */
static Bool
xf86AiptekReverseConvert(LocalDevicePtr local,
                         int            x,
                         int            y,
                         int*           valuators)
{
    AiptekDevicePtr device = (AiptekDevicePtr) local->private;
    int    xSize, ySize;

    xf86Msg(X_CONFIG, ": xf86AiptekReverseConvert(), with: x=%d, y=%d, "
            "valuators[0]=%d, valuators[1]=%d\n",
            x, y, valuators[0], valuators[1] );

    /*
     * Adjust by tablet ratio
     */
    xSize = device->xBottom - device->xTop;
    ySize = device->yBottom - device->yTop;

    valuators[0] = (x*xSize) / screenInfo.screens[device->screenNo]->width;
    valuators[1] = (y*ySize) / screenInfo.screens[device->screenNo]->height;

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

    if (device->screenNo != 0)
    {
        xf86XInputSetScreen(local,device->screenNo,valuators[0], valuators[1]);
    }
    xf86Msg(X_CONFIG, ": xf86AiptekReverseConvert() exits, with: "
            "valuators[0]=%d, valuators[1]=%d\n",
            valuators[0], valuators[1] );

    return TRUE;
}

/**********************************************************************
 * xf86AiptekSendEvents
 *  Send events according to the device state.
 */
static void
xf86AiptekSendEvents(LocalDevicePtr local, int r_z)
{
    AiptekDevicePtr device = (AiptekDevicePtr) local->private;
    AiptekCommonPtr common = device->common;

    int bCorePointer, bAbsolute;
    int x, y, z, xTilt, yTilt;

    if ((DEVICE_ID(device->flags) != common->currentValues.eventType))
    {
        DBG(7,ErrorF("xf86AiptekSendEvents: not the same device type (%u,%u)\n",
           DEVICE_ID(device->flags), common->currentValues.eventType));
        return;
    }

    bAbsolute    = (device->flags & ABSOLUTE_FLAG);
    bCorePointer = xf86IsCorePointer(local->dev);

    /*
     * Normalize X and Y coordinates. This includes dealing
     * with absolute/relative coordinate mode.
     */
    if (bAbsolute)
    {
        x = common->currentValues.x;
        y = common->currentValues.y;
        z = r_z;
        xTilt = common->currentValues.xTilt;
        yTilt = common->currentValues.yTilt;
    }
    else
    {
        x = common->currentValues.x - common->previousValues.x;
        y = common->currentValues.y - common->previousValues.y;
        z = r_z - common->previousValues.z;
        xTilt = common->currentValues.xTilt - common->previousValues.xTilt;
        yTilt = common->currentValues.yTilt - common->previousValues.yTilt;
    }

    /* Translate coordinates according to Top and Bottom points.
     */
    if (x > device->xBottom) {
        x = device->xBottom;
    }

    if (y > device->yBottom) {
        y = device->yBottom;
    }

    if (device->xTop > 0) {
        DBG(10, ErrorF("Adjusting x, with xTop=%d\n", device->xTop));
        x -= device->xTop;
    }

    if (device->yTop > 0) {
        DBG(10, ErrorF("Adjusting y, with yTop=%d\n", device->yTop));
        y -= device->yTop;
    }

    if (x < 0) {
        x = 0;
    }
    if (y < 0) {
        y = 0;
    }

    /* Deal with pressure min..max, which differs from threshold. */
    if (z < device->zMin) {
        z = 0;
    }

    if (z > device->zMax) {
        z = device->zMax;
    }

    /*
     * First, handle the macro keys.
     */
    if (common->currentValues.macroKey != VALUE_NA)
    {
        int i;

        /* This is a little silly, but: The Linux Event Input
         * system uses a slightly different keymap than does X 
         * (it also has more keys defined). So, we have to go
         * through a translation process. It's made sillier than
         * required because X wants an offset to it's KeySym table,
         * rather than an event key -- it'll do it's own lookup.
         * It DOES support arbitrary ordering of key events, and
         * partial keyboard matrices, so that speaks in favor of this
         * scheme.
         */
        for (i = 0; 
             i < sizeof(linux_inputDevice_keyMap)/
                 sizeof(linux_inputDevice_keyMap[0]);
             ++i)
        {
            if (linux_inputDevice_keyMap[0]==common->currentValues.macroKey)
            {
                break;
            }
        }

        /* First available Keycode begins at 8 => macro+7.
         * It's pervasive throughout the Xinput drivers, and
         * no, I don't know why they purposively waste the first 8
         * positions of the KeySym map...
         */

        /* Keyboard 'make' (press) event */
        xf86PostKeyEvent(local->dev, i+7, TRUE,
                         bAbsolute, 0, 5,
                         x, y, common->currentValues.button, xTilt, yTilt);
        /* Keyboard 'break' (depress) event */
        xf86PostKeyEvent(local->dev, i+7, FALSE,
                         bAbsolute, 0, 5,
                         x, y, common->currentValues.button, xTilt, yTilt);
    }

    /* As the coordinates are ready, we can send events to X */
    if (common->currentValues.proximity)
    {
        if (!common->previousValues.proximity)
        {
            if (!bCorePointer)
            {
                xf86PostProximityEvent(local->dev, 1, 0, 5,
                    x, y, z, xTilt, yTilt);
            }
        }

        if ((bAbsolute &&
             (common->previousValues.x != common->currentValues.x ||
              common->previousValues.y != common->currentValues.y ||
              common->previousValues.z != common->currentValues.z)) ||
            (!bAbsolute && 
             (common->currentValues.x || common->currentValues.y)))
        {
            if (bAbsolute || common->previousValues.proximity)
            {
                xf86PostMotionEvent(local->dev, bAbsolute, 0, 5,
                        x, y, z, xTilt, yTilt);
            }
        }
       
        if (common->previousValues.button != common->currentValues.button)
        {
            int delta;
            delta = common->currentValues.button ^ common->previousValues.button;
            while(delta)
            {
                int id;
                id = ffs(delta);
                delta &= ~(1 << (id-1));
                xf86PostButtonEvent(local->dev, bAbsolute, id,
                        (common->currentValues.button & (1<<(id-1))), 0, 5,
                        x, y, z, xTilt, yTilt);
            }
        }
    }
    else
    {
        if (!bCorePointer)
        {
            if (common->previousValues.proximity)
            {
                xf86PostProximityEvent(local->dev, 0, 0, 5, x, y, z,
                        xTilt, yTilt);
            }
        }
        common->previousValues.proximity = 0;
    }
}

/*
 ***************************************************************************
 * xf86AiptekHIDReadInput --
 *    Read the new events from the device, and enqueue them.
 */
static void
xf86AiptekHIDReadInput(LocalDevicePtr local)
{
    AiptekDevicePtr     device = (AiptekDevicePtr) local->private;
    AiptekCommonPtr     common = device->common;

    ssize_t             len;
    int                 i;
    struct input_event* event;
    char                eventbuf[sizeof(struct input_event) * MAX_EVENTS];
    int                 eventsInMessage;
    double              d_z;
    double              d_zCapacity;

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

    if (len <= 0)
    {
        ErrorF("Error reading Aiptek tablet: %s\n", strerror(errno));
        return;
    }

    eventsInMessage = 0;
    for (event=(struct input_event *)(eventbuf);
         event<(struct input_event *)(eventbuf+len);
         ++event)
    {
        /*
         * Unprocessed events:
         * ABS_RZ - rotate stylus
         * ABS_DISTANCE - unknown
         * ABS_THROTTLE - unknown
         * ABS_WHEEL - we have no wheel
         * REL_WHEEL - we have no wheel
         *
         * Synthesized events
         * ABS_X_TILT - The aiptek tablet does not report these,
         * ABS_Y_TILT - but the Linux kernel driver sends synthetic values.
         */
        switch (event->type)
        {
            case EV_ABS:
            {
                switch (event->code)
                {
                    case ABS_X:
                    {
                        ++eventsInMessage;
                        common->currentValues.x = event->value;
                    }
                    break;

                    case ABS_Y:
                    {
                        ++eventsInMessage;
                        common->currentValues.y = event->value;
                    }
                    break;

                    case ABS_PRESSURE:
                    {
                        ++eventsInMessage;
                        common->currentValues.z = event->value;
                    }
                    break;

                    case ABS_TILT_X:
                    case ABS_RZ:
                    {
                        ++eventsInMessage;
                        common->currentValues.xTilt = event->value;
                    }
                    break;

                    case ABS_TILT_Y:
                    {
                        ++eventsInMessage;
                        common->currentValues.yTilt = event->value;
                    }
                    break;

                    case ABS_DISTANCE:
                    {
                        ++eventsInMessage;
                        common->currentValues.distance = event->value;
                    }
                    break;

                    case ABS_WHEEL:
                    case ABS_THROTTLE:
                    {
                        ++eventsInMessage;
                        common->currentValues.wheel = event->value;
                    }
                    break;

                    case ABS_MISC:
                    {
                        /* We have an agreement with the
                         * Linux Aiptek HID driver to send
                         * the proximity bit through ABS_MISC.
                         * We do this solely if proximity is
                         * being reported through the Stylus tool;
                         * else, if mouse, we'll get proximity through
                         * REL_MISC.
                         */
                        ++eventsInMessage;
                        common->currentValues.proximity = 
                            (event->value > 0 ? 1 : 0);
                    }
                    break;
                }
            }
            break; /* EV_ABS */

            case EV_REL:
            {
                switch (event->code)
                {
                    case REL_X:
                    {
                        /* Normalize all relative events into absolute
                         * coordinates.
                         */
                        ++eventsInMessage;
                        common->currentValues.x = 
                            common->previousValues.x + event->value;
                    }
                    break;

                    case REL_Y:
                    {
                        /* Normalize all relative events into absolute
                         * coordinates.
                         */
                        ++eventsInMessage;
                        common->currentValues.y =
                            common->previousValues.y + event->value;
                    }
                    break;

                    case REL_WHEEL:
                    {
                        /* Normalize all relative events into absolute
                         * coordinates.
                         */
                        ++eventsInMessage;
                        common->currentValues.wheel =
                            common->previousValues.wheel + event->value;
                    }

                    case REL_MISC:
                    {
                        /* We have an agreement with the
                         * Linux Aiptek HID driver to send
                         * the proximity bit through REL_MISC.
                         * We do this solely if proximity is
                         * being reported through the Mouse tool;
                         * else, if stylus, we'll get proximity through
                         * ABS_MISC.
                         */
                        ++eventsInMessage;
                        common->currentValues.proximity = 
                            (event->value > 0 ? 1 : 0);
                    }
                    break;
                }
            }
            break; /* EV_REL */

            case EV_KEY:
            {
                switch (event->code)
                {
                    /*
                     * Events begun with a BTN_TOOL_PEN, PENCIL,
                     * BRUSH or AIRBRUSH indicate that they are
                     * destined for the STYLUS device.
                     *
                     * This should probably change, and we should
                     * have devices for each type. We'll address that
                     * later.
                     */
                    case BTN_TOOL_PEN:
                    case BTN_TOOL_PENCIL:
                    case BTN_TOOL_BRUSH:
                    case BTN_TOOL_AIRBRUSH:
                    {
                        ++eventsInMessage;
                        common->currentValues.eventType = STYLUS_ID;
                    }
                    break;

                    /*
                     * Events begun with a BTN_TOOL_RUBBER indicate
                     * that they are destined for the ERASER device.
                     */
                    case BTN_TOOL_RUBBER:
                    {
                        ++eventsInMessage;
                        common->currentValues.eventType = ERASER_ID;
                    }
                    break;

                    /*
                     * A TOOL_LENS would be for a true PUCK/CURSOR.
                     * Aiptek instead gives us a mouse, that we can pretend
                     * is a puck.
                     */
                    case BTN_TOOL_MOUSE:
                    case BTN_TOOL_LENS:
                    {
                        ++eventsInMessage;
                        common->currentValues.eventType = CURSOR_ID;
                    }
                    break;

                    /*
                     * Normal button handling: TOUCH, STYLUS and
                     * STYLUS2 all buttons that we'll report to X
                     * as normal buttons.
                     */
                    case BTN_TOUCH:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_TOUCH;
                    }
                    break;

                    case BTN_STYLUS:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_STYLUS;
                    }
                    break;

                    case BTN_STYLUS2:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_STYLUS2;
                    }
                    break;

                    /*
                     * Normal Mouse button handling: LEFT, RIGHT and
                     * MIDDLE all buttons that we'll report to X
                     * as normal buttons. Note that the damned things
                     * re-use the same bitmasks as the Stylus buttons,
                     * above.
                     */
                    case BTN_LEFT:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_LEFT;
                    }
                    break;

                    case BTN_MIDDLE:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_MIDDLE;
                    }
                    break;

                    case BTN_RIGHT:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_RIGHT;
                    }
                    break;

                    case BTN_SIDE:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_SIDE_BTN;
                    }
                    break;

                    case BTN_EXTRA:
                    {
                        ++eventsInMessage;
                        common->currentValues.button |= 
                            (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_EXTRA_BTN;
                    }
                    break;

                    /*
                     * Any other EV_KEY event is assumed to be
                     * the pressing of a macro key from the tablet.
                     */
                    default:
                    {
                        ++eventsInMessage;
                        common->currentValues.macroKey = event->value;
                    }
                    break;
                }
            }
            break; /* EV_KEY */

        } /* switch event->type */

        /* We have two potential event terminators. EV_MSC was used
         * by (unwritten) convention to indicate the end-of-report.
         * Problem is, EV_MSC is supposed to actually report data,
         * so a new event type, EV_SYN, was created in Linux 2.5.x
         * expressively for this purpose.
         *
         * Theoretically, if EV_SYN is defined, I should only terminate
         * the population of device->currentValues struct IFF I receive
         * that event. The fact of the matter is, the EV_MSC is assumed
         * to be an ugliness that will take some time to be deprecated.
         * For the nonce, we'll support both. But, if you have a tablet
         * that's actually supplying something interesting with EV_MSC,
         * this is obviously some code that requires modifications.
         */
#ifndef EV_SYN
        if (event->type != EV_MSC)
#else
        if (event->type != EV_MSC && event->type != EV_SYN)
#endif
        {
            continue;
        }

        /*
         * We've seen EV_MSCs in the incoming data trail with no
         * other message types in-between. We use 'eventsInMessage'
         * to count all 'real' messages in-between. If there were none,
         * do NOT copy common->currentValues to common->previousValues
         * (as this will make the jitter filter useless). Just go and
         * read the subsequent events.
         */
        if (eventsInMessage == 0)
        {
            continue;
        }
        eventsInMessage = 0;

        /* 
         * This filter throws out reports that do not meet minimum threshold
         * requirements for movement along that axis.
         *
         * Presently, we discard the entire report if any dimension of the
         * currentValues struct does not meet it's minimum threshold.
         *
         * Also, we only do the comparison IFF a threshold has been set
         * for that given dimension.
         */
        if ((device->xThreshold > 1 && 
                ABS(common->currentValues.x - common->previousValues.x)
                    <= device->xThreshold) ||
            (device->yThreshold > 1 &&
                ABS(common->currentValues.y - common->previousValues.y)
                    <= device->yThreshold) ||
            (device->zThreshold > 1 &&
                ABS(common->currentValues.z - common->previousValues.z)
                    <= device->zThreshold) ||
            (device->xTiltThreshold > 1 &&
                ABS(common->currentValues.xTilt - common->previousValues.xTilt)
                    <= device->xTiltThreshold) ||
            (device->yTiltThreshold > 1 &&
                ABS(common->currentValues.yTilt - common->previousValues.yTilt)
                    <= device->yTiltThreshold))
        {
            DBG(10, ErrorF("Event Filtered Out by Thresholds\n"));
            continue;
        }

        /*
         * If this report somehow has exactly the same readings as the
         * previous report for all dimensions, throw the report out.
         */
        if ((common->currentValues.x == common->previousValues.x) &&
            (common->currentValues.y == common->previousValues.y) &&
            (common->currentValues.z == common->previousValues.z) &&
            (common->currentValues.proximity == 
                                         common->previousValues.proximity) &&
            (common->currentValues.button == 
                                         common->previousValues.button) &&
            (common->currentValues.macroKey ==
                                         common->previousValues.macroKey))
        {
            DBG(10, ErrorF("Event Filtered Out\n"));
            continue;
        }

        /*
         * We have three different methods by which we report pressure, Z.
         * One is to use linear values from 0 to common->zCapacity. The
         * other two, SOFT_SMOOTH and HARD_SMOOTH, use different
         * algorithms to 'smooth out' the values.
         */
        d_z         = (double)common->currentValues.z;
        d_zCapacity = (double)common->zCapacity;

        switch (device->zMode)
        {
            case VALUE_NA:
            case PRESSURE_MODE_LINEAR:
            {
                /* Leave Z alone. */
            }
            break;

            case PRESSURE_MODE_SOFT_SMOOTH:
            {
                d_z = (d_z * d_z / d_zCapacity)+ 0.5;
            }
            break;

            case PRESSURE_MODE_HARD_SMOOTH:
            {
                d_z = (d_zCapacity * sqrt( d_z / d_zCapacity)) + 0.5;
            }
            break;
        }

        /* Dispatch events to all of our configured devices. */
        for (i=0; i < common->numDevices; ++i)
        {
            AiptekDevicePtr dev = common->deviceArray[i]->private;
            int             id;

            id  = DEVICE_ID (dev->flags);

            /* Find the device the current events are meant for */
            if (id == common->currentValues.eventType)
            {
                /* We left 'z' alone during smoothing, so send up
                 * perturbed value outside of the struct
                 */
                xf86AiptekSendEvents(common->deviceArray[i], (int) d_z);
            }
        }

        /*
         * Copy the values just processed into the previousValues struct,
         * so we can check for 'jittering' in the subsequent report.
         */
        common->previousValues.eventType = common->currentValues.eventType;
        common->previousValues.x         = common->currentValues.x;
        common->previousValues.y         = common->currentValues.y;
        common->previousValues.z         = common->currentValues.z;
        common->previousValues.proximity = common->currentValues.proximity;
        common->previousValues.button    = common->currentValues.button;
        common->previousValues.macroKey  = common->currentValues.macroKey;
        common->previousValues.xTilt     = common->currentValues.xTilt;
        common->previousValues.yTilt     = common->currentValues.yTilt;
        common->previousValues.distance  = common->currentValues.distance;
        common->previousValues.wheel     = common->currentValues.wheel;

        common->currentValues.macroKey   = VALUE_NA;
    }
}

/*
 ***************************************************************************
 *
 * xf86AiptekHIDOpen --
 *
 ***************************************************************************
 */
static Bool
xf86AiptekHIDOpen(LocalDevicePtr local)
{
    AiptekDevicePtr device = (AiptekDevicePtr)local->private;
    AiptekCommonPtr common = device->common;
    char            name[256] = "Unknown";
    int             abs[5];
    unsigned long   bit[EV_MAX][NBITS(KEY_MAX)];
    int             i, j;
    int             err = 0;
    int             version;

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

    ioctl(local->fd, EVIOCGNAME(sizeof(name)), name);
    ErrorF("%s HID Device name: \"%s\"\n", XCONFIG_PROBED, name);

    ioctl(local->fd, EVIOCGVERSION, &version);
    ErrorF("%s HID Driver Version: %d.%d.%d\n", XCONFIG_PROBED,
            version>>16, (version>>8) & 0xff, version & 0xff);

    ErrorF("%s HID Driver knows it has %d devices configured\n", XCONFIG_PROBED,
            common->numDevices);
    ErrorF("%s HID Driver is using %d as the fd\n", XCONFIG_PROBED, local->fd);

    for (i = 0; i < common->numDevices; ++i)
    {
        common->deviceArray[i]->read_input = xf86AiptekHIDReadInput;
        common->deviceArray[i]->fd = local->fd;
        common->deviceArray[i]->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
    }
    common->open = xf86AiptekHIDOpen;

    memset(bit, 0, sizeof(bit));
    ioctl(local->fd, EVIOCGBIT(0, EV_MAX), bit[0]);

    for (i = 0; i < EV_MAX; ++i)
    {
        if (TEST_BIT(i, bit[0]))
        {
            ioctl(local->fd, EVIOCGBIT(i, KEY_MAX), bit[i]);
            for (j = 0; j < KEY_MAX; ++j)
            {
                if (TEST_BIT(j, bit[i]))
                {
                    if (i == EV_ABS)
                    {
                        ioctl(local->fd, EVIOCGABS(j), abs);
                        switch (j) 
                        {
                            case ABS_X:
                            {
                                ErrorF("From ioctl() xCapacity=%d\n", abs[2]);
                                common->xCapacity = abs[2];
                            }
                            break;

                            case ABS_Y:
                            {
                                ErrorF("From ioctl() yCapacity=%d\n", abs[2]);
                                common->yCapacity = abs[2];
                            }
                            break;

                            case ABS_Z:
                            {
                                ErrorF("From ioctl() zCapacity=%d\n", abs[2]);
                                common->zCapacity = abs[2];
                            }
                            break;
                        }
                    }
                }
            }
        }
    }

    if (err < 0)
    {
        ErrorF("xf86AiptekHIDOpen ERROR: %d\n", err);
        SYSCALL(close(local->fd));
        return !Success;
    }

    return Success;
}

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

/*
** xf86AiptekOpen
 * Open and initialize the tablet, as well as probe for any needed data.
 * (This is TTY style open)
 */

static Bool
xf86AiptekOpen(LocalDevicePtr local)
{
    AiptekDevicePtr device = (AiptekDevicePtr)local->private;
    AiptekCommonPtr common = device->common;
    int err, version;

    DBG(1, ErrorF("Opening %s\n", common->deviceName));

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

    DBG(1, ErrorF("Testing USB\n"));

    SYSCALL(err = ioctl(local->fd, EVIOCGVERSION, &version));
    if (!err)
    {
        int j;

        SYSCALL(close(local->fd));

        for(j=0; j<common->numDevices; ++j)
        {
            common->deviceArray[j]->read_input=xf86AiptekHIDReadInput;
        }
        common->open=xf86AiptekHIDOpen;

        return xf86AiptekHIDOpen(local);
    }

    /* We do not support TTY mode, so just exit angry. */
    return !Success;
}

/*
 * xf86AiptekOpenDevice
 * Opens and initializes the device driver.
 */
static int
xf86AiptekOpenDevice(DeviceIntPtr pDriver)
{
    LocalDevicePtr    local  = (LocalDevicePtr)pDriver->public.devicePrivate;
    AiptekDevicePtr   device = (AiptekDevicePtr)PRIVATE(pDriver);
    AiptekCommonPtr   common = device->common;
    double            tabletRatio, screenRatio;
    double            xFactor, yFactor;
    int               gap, loop;

    DBG(2, ErrorF("In xf86AiptekOpenDevice, with fd=%d\n", local->fd));

    if (local->fd < 0)
    {
        if (common->initNumber > 2 ||
            device->initNumber == common->initNumber)
        {
            if (common->open(local) != Success)
            {
                if (local->fd >= 0)
                {
                    SYSCALL(close(local->fd));
                }
                local->fd = -1;
                return !Success;
            }
            else
            {
                /* Report the file descriptor to all devices */
                for (loop=0; loop < common->numDevices; ++loop)
                {
                    common->deviceArray[loop]->fd = local->fd;
                }
            }
            common->initNumber++;
        }
        device->initNumber = common->initNumber;
    }

    /*
     * Check our active area parameters. We support the following
     * three sets of mutually exclusive parameter sets:
     * 1) xMax/yMax. The origin (0,0) of the active area is the origin
     *    of the physical tablet. You therefore are describing the
     *    width and height of that active area.
     * 2) xOffset/xSize,yOffset/ySize. The origin (0,0) of the active
     *    area is defined as (xOffset,yOffset) (which we'll report as
     *    (0,0)). The size of the active area in width and height are
     *    expressed in coordinates as xSize/ySize. That is to say,
     *    if xOffset=5; yOffset=5, and xSize=10; ySize=10, then we will 
     *    have an active area beginning at (5,5) and ending at (15,15).
     *    Physical coordinate (5,5) is reported as (0,0); (15,15) is 
     *    reported as (10,10). The rest of the tablet is inert, as far as
     *    drawing area goes,
     * 3) xTop/xBottom,yTop/yBottom. The difference between this and
     *    #2 above is that all four parameters are physical coordinates
     *    on the tablet. Using the example above, xTop=5; yTop=5, and
     *    xBottom=15; yBottom=15. It is inferred mathematically that
     *    the overall active area is 10x10 coordinates.
     *
     * NOTE: Mutually exclusive means just that: choose the set of
     * parameters you like, and use them throughout. If you user xSize,
     * yOffset and xBottom, we'll have NO idea what you want, and quite
     * frankly you'll have tempted Fate enough that Bad Things(tm) will
     * happen to you. Do not complain to us!
     */
    if (device->xMax != VALUE_NA ||
        device->yMax != VALUE_NA)
    {
        /* Deal with larger-than-tablet and NA values.
         */
        if (device->xMax >  common->xCapacity ||
            device->xMax == VALUE_NA)
        {
            device->xMax = common->xCapacity;
            xf86Msg(X_CONFIG, "xMax value invalid; adjusting to %d\n",
                    device->xMax);
        }
        if (device->yMax > common->yCapacity ||
            device->yMax == VALUE_NA)
        {
            device->yMax = common->yCapacity;
            xf86Msg(X_CONFIG,"yMax value invalid; adjusting to %d\n",
                    device->yMax);
        }

        /*
         * Internally we use xTop/yTop/xBottom/yBottom
         * for everything. It's the easiest for us to work
         * with, vis-a-vis filtering.
         */

        device->xTop = 0;
        device->yTop = 0;
        device->xBottom = device->xMax;
        device->yBottom = device->yMax;
    }

    /*
     * Deal with xOffset/yOffset;xSize/ySize parameters
     */
    if (device->xSize   != VALUE_NA ||
        device->ySize   != VALUE_NA ||
        device->xOffset != VALUE_NA ||
        device->yOffset != VALUE_NA)
    {
        int message = 0;
        /* Simple sanity tests: nothing larger than the
         * tablet; nothing negative, except for an NA value.
         */
        if (device->xOffset  != VALUE_NA &&
            (device->xOffset >  common->xCapacity ||
             device->xOffset <  0))
        {
            message = 1;
            device->xOffset = 0;
        }
        if (device->yOffset  != VALUE_NA &&
            (device->yOffset >  common->yCapacity ||
             device->yOffset <  0))
        {
            message = 1;
            device->yOffset = 0;
        }
        if (device->xSize  != VALUE_NA &&
            (device->xSize >  common->xCapacity ||
             device->xSize <  0))
        {
            message = 1;
            device->xSize = common->xCapacity;
        }
        if (device->ySize  != VALUE_NA &&
            (device->ySize >  common->yCapacity ||
             device->ySize <  0))
        {
            message = 1;
            device->ySize = common->yCapacity;
        }

        /*
         * If one parameter is set but not the other, we'll
         * guess at something reasonable for the missing one.
         */
        if (device->xOffset == VALUE_NA ||
            device->xSize   == VALUE_NA)
        {
            if (device->xOffset == VALUE_NA)
            {
                message = 1;
                device->xOffset = 0;
            }
            else
            {
                message = 1;
                device->xSize = common->xCapacity - device->xOffset;
            }
        }
        if (device->yOffset == VALUE_NA ||
            device->ySize   == VALUE_NA)
        {
            if (device->yOffset == VALUE_NA)
            {
                message = 1;
                device->yOffset = 0;
            }
            else
            {
                message = 1;
                device->ySize = common->yCapacity - device->yOffset;
            }
        }

        /*
         * Do not allow the active area to exceed the size of the
         * tablet. To do this, we have to consider both parameters.
         * Assumption: xOffset/yOffset is always correct; deliver less
         * of the tablet than they asked for, if they asked for too much.
         */
        if (device->xOffset + device->xSize > common->xCapacity)
        {
            message = 1;
            device->xSize = common->xCapacity - device->xOffset;
        }
        if (device->yOffset + device->ySize > common->yCapacity)
        {
            message = 1;
            device->ySize = common->yCapacity - device->yOffset;
        }

        /*
         * 'message' is used to indicate that we've changed some parameter
         * during our filtration process. It's conceivable that we may
         * have changed parameters several times, so we without commentary
         * to the very end.
         */
        if (message == 1)
        {
            xf86Msg(X_CONFIG,"xOffset/yOffset;xSize/ySize values wrong.\n");
            xf86Msg(X_CONFIG,"xOffset adjusted to %d\n", device->xOffset);
            xf86Msg(X_CONFIG,"yOffset adjusted to %d\n", device->yOffset);
            xf86Msg(X_CONFIG,"xSize adjusted to %d\n", device->xSize);
            xf86Msg(X_CONFIG,"ySize adjusted to %d\n", device->ySize);
        }

        /*
         * Internally we use xTop/yTop/xBottom/yBottom
         * for everything. It's the easiest for us to work
         * with, vis-a-vis filtering.
         */
        device->xTop    = device->xOffset;
        device->yTop    = device->yOffset;
        device->xBottom = device->xOffset + device->xSize;
        device->yBottom = device->yOffset + device->ySize;
    }

    /*
     * Third set of parameters. Because everything internally
     * is expressed as xTop/yTop, etc., I'll do tests on transformed
     * values from the other parameters as need. My last chance to do
     * so.
     */
    if (device->xTop == VALUE_NA ||
        device->xTop <  0 ||
        device->xTop >  common->xCapacity)
    {
        device->xTop = 0;
        xf86Msg(X_CONFIG,"xTop invalid; adjusted to %d\n", device->xTop);
    }
    if (device->yTop == VALUE_NA ||
        device->yTop <  0 ||
        device->yTop >  common->yCapacity)
    {
        device->yTop = 0;
        xf86Msg(X_CONFIG,"yTop invalid; adjusted to %d\n", device->yTop);
    }
    if (device->xBottom == VALUE_NA ||
        device->xBottom <  0 ||
        device->xBottom >  common->xCapacity)
    {
        device->xBottom = common->xCapacity;
        xf86Msg(X_CONFIG,"xBottom invalid; adjusted to %d\n", 
                device->xBottom);
    }
    if (device->yBottom == VALUE_NA ||
        device->yBottom <  0 ||
        device->yBottom >  common->yCapacity)
    {
        device->yBottom = common->yCapacity;
        xf86Msg(X_CONFIG,"yBottom invalid; adjusted to %d\n", 
                    device->yBottom);
    }

    /*
     * Determine the X screen we're going to be using.
     * If NA, or larger than the number of screens we
     * have, or negative, we've going for screen 0, e.g.,
     * 'default' screen.
     */
    if ( device->screenNo >= screenInfo.numScreens ||
         device->screenNo == VALUE_NA ||
         device->screenNo < 0) 
    {
        device->screenNo = 0;
        xf86Msg(X_CONFIG,"ScreenNo invalid; adjusted to %d\n", 
                device->screenNo);
    }

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

    if (device->flags & KEEP_SHAPE_FLAG)
    {
        int xDiff, yDiff;

        xDiff = common->xCapacity - device->xTop;
        yDiff = common->yCapacity - device->yTop;

        tabletRatio = (double) xDiff / (double) yDiff;
        screenRatio = (double) screenInfo.screens[device->screenNo]->width /
                      (double) screenInfo.screens[device->screenNo]->height;

        DBG(2, ErrorF("Screen %d: screenRatio = %.3g, tabletRatio = %.3g\n",
                  device->screenNo, screenRatio, tabletRatio));

        if (screenRatio > tabletRatio)
        {
            gap = (int)((double)common->yCapacity *
                       (1.0 - tabletRatio/screenRatio));
            device->xBottom = common->xCapacity;
            device->yBottom = common->yCapacity - gap;
            DBG(2, ErrorF("Screen %d: 'Y' Gap of %d computed\n",
                  device->screenNo, gap));
        }
        else
        {
            gap = (int)((double)common->xCapacity *
                       (1.0 - screenRatio/tabletRatio));
            device->xBottom = common->xCapacity - gap;
            device->yBottom = common->yCapacity;
            DBG(2, ErrorF("Screen %d: 'X' Gap of %d computed\n",
                  device->screenNo, gap));
        }
    }

    xFactor = (double)screenInfo.screens[device->screenNo]->width/
        (double)(device->xBottom - device->xTop);
    yFactor = (double)screenInfo.screens[device->screenNo]->height/
        (double)(device->yBottom - device->yTop);

    /* 
     * Check threshold correctness
     */
    if (device->xThreshold > common->xCapacity ||
        device->xThreshold == VALUE_NA ||
        device->xThreshold <  0)
    {
        device->xThreshold = 0;
    }

    if (device->yThreshold > common->yCapacity ||
        device->yThreshold == VALUE_NA ||
        device->yThreshold < 0)
    {
        device->yThreshold = 0;
    }

    if (device->zThreshold > common->zCapacity ||
        device->zThreshold == VALUE_NA ||
        device->zThreshold < 0) 
    {
        device->zThreshold = 0;
    }

    /* Create axisStructs for every axis we support.
     * NOTE: min_resolution and max_resolution infers to
     * me a programmability to increase/decrease resolution.
     * We don't support that, so min & max = current_resolution.
     */
    InitValuatorAxisStruct(pDriver,                 /* X resolution */
               0,                                   /* axis_id */
               0,                                   /* min value */
               device->xBottom - device->xTop,      /* max value */
               LPI2CPM(375),                        /* resolution */
               LPI2CPM(375),                        /* min_resolution */
               LPI2CPM(375));                       /* max_resolution */

    InitValuatorAxisStruct(pDriver,                 /* Y Resolution */
               1,                                   /* axis_id */
               0,                                   /* min value */
               device->yBottom - device->yTop,      /* max value */
               LPI2CPM(375),                        /* resolution */
               LPI2CPM(375),                        /* min_resolution */
               LPI2CPM(375));                       /* max_resolution */

    InitValuatorAxisStruct(pDriver,                 /* Pressure */
               2,                                   /* axis_id */
               0,                                   /* min value */
               511,                                 /* max value */
               512,                                 /* resolution */
               512,                                 /* min_resolution */
               512);                                /* max_resolution */

    InitValuatorAxisStruct(pDriver,                 /* xTilt */
               3,                                   /* axis id */
               -128,                                /* min value */
               127,                                 /* max value */
               256,                                 /* resolution */
               256,                                 /* min_resolution */
               256);                                /* max_resolution */

    InitValuatorAxisStruct(pDriver,                 /* yTilt */
               4,                                   /* axis_id */
               -128,                                /* min value */
               127,                                 /* max value */
               256,                                 /* resolution */
               256,                                 /* min_resolution */
               256);                                /* max_resolution */

    /*
     * The sixth axis would be for wheel support. We do not have
     * any wheel devices. But if we did, it would be allocated
     * here.
     */
    return (local->fd != -1);
}

/*
 * xf86AiptekProc
 *
 *  Call dispatcher for this driver.
 */
static int
xf86AiptekProc(DeviceIntPtr pAiptek, int requestCode)
{
    CARD8           map[512+1];
    int             numAxes;
    int             numButtons;
    int             loop;
    LocalDevicePtr  local  = (LocalDevicePtr)pAiptek->public.devicePrivate;
    AiptekDevicePtr device = (AiptekDevicePtr)PRIVATE(pAiptek);

    DBG(2, ErrorF("xf86AiptekProc() type=%s flags=%d request=%d\n",
              (DEVICE_ID(device->flags) == STYLUS_ID) ? "stylus" :
              (DEVICE_ID(device->flags) == CURSOR_ID) ? "cursor" : "eraser",
              device->flags, requestCode));

    switch (requestCode)
    {
        case DEVICE_INIT:
        {
            DBG(1, ErrorF("xf86AiptekProc request=INIT\n"));
            numAxes    = 5;            /* X, Y, Z, xTilt, yTilt */
            numButtons = 5;

            for(loop=1; loop<=numButtons; ++loop)
            {
                map[loop] = loop;
            }

            if (InitButtonClassDeviceStruct(pAiptek,numButtons,map) == FALSE)
            {
                ErrorF("Unable to init Button Class Device\n");
                return !Success;
            }

            if (InitFocusClassDeviceStruct(pAiptek) == FALSE) 
            {
                ErrorF("Unable to init Focus Class Device\n");
                return !Success;
            }

            if (InitPtrFeedbackClassDeviceStruct(pAiptek,
                        xf86AiptekControlProc) == FALSE)
            {
                ErrorF("Unable to init Pointer Feedback Class Device\n");
                return !Success;
            }

            if (InitProximityClassDeviceStruct(pAiptek) == FALSE)
            {
                ErrorF("Unable to init Proximity Class Device\n");
                return !Success;
            }

            if (InitKeyClassDeviceStruct(pAiptek, &keysyms, NULL) ==FALSE)
            {
                ErrorF("Unable to init Key Class Device\n");
                return !Success;
            }

            if (InitValuatorClassDeviceStruct(pAiptek,
                   numAxes,
                   xf86GetMotionEvents,
                   local->history_size,
                   ((device->flags & ABSOLUTE_FLAG) 
                        ? Absolute : Relative) | OutOfProximity ) == FALSE)
            {
                ErrorF("Unable to allocate Valuator Class Device\n");
                return !Success;
            }

            /* Allocate the motion history buffer if needed */
            xf86MotionHistoryAllocate(local);

            /* Open the device to gather information */
            xf86AiptekOpenDevice(pAiptek);
        }
        break;

        case DEVICE_ON:
        {
            DBG(1, ErrorF("xf86AiptekProc request=ON\n"));

            if ((local->fd < 0) && 
                (!xf86AiptekOpenDevice(pAiptek)))
            {
                ErrorF("Unable to open aiptek device\n");
                return !Success;
            }
            ErrorF("Able to open aiptek device\n");
            xf86AddEnabledDevice(local);
            pAiptek->public.on = TRUE;
        }
        break;

        case DEVICE_OFF:
        {
            DBG(1, ErrorF("xf86AiptekProc request=%s\n", 
                            (requestCode == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
            if (local->fd >= 0)
            {
                xf86AiptekClose(local);
                xf86RemoveEnabledDevice(local);
            }
            pAiptek->public.on = FALSE;
        }
        break;

        case DEVICE_CLOSE:
        {
            DBG(1, ErrorF("xf86AiptekProc request=%s\n", 
                           (requestCode == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
            xf86AiptekClose(local);
        }
        break;

        default:
        {
            ErrorF("xf86AiptekProc - Unsupported mode=%d\n", requestCode);
            return !Success;
        }
        break;
    }
    DBG(2, ErrorF("xf86AiptekProc Success request=%d\n", requestCode ));
    return Success;
}

/*
 * xf86AiptekClose
 * Perhaps this will close the device
 */
static void
xf86AiptekClose(LocalDevicePtr local)
{
    if (local->fd >= 0)
    {
        SYSCALL(close(local->fd));
    }
    local->fd = -1;
}

/*
 * xf86AiptekChangeControl
 * Allow the user to change the tablet resolution -- we have an issue
 * insofar as we don't know how to write to the tablet. And furthermore,
 * even if we DID know how to write to the tablet, it doesn't support
 * a "change resolution" call. We tried to avoid this by claiming when
 * creating axisStructs that minRes = curRes = maxRes. So, we should never
 * get dispatched.
 */
static int
xf86AiptekChangeControl(LocalDevicePtr local, xDeviceCtl *control)
{
    xDeviceResolutionCtl    *res;
    int                     *resolutions;

    DBG(3, ErrorF("xf86AiptekChangeControl() entered\n"));

    res = (xDeviceResolutionCtl *)control;
    
    if ((control->control != DEVICE_RESOLUTION) ||
        (res->num_valuators < 1))
    {
        DBG(3, ErrorF("xf86AiptekChangeControl abends\n"));
        return (BadMatch);
    }

    resolutions = (int *)(res +1);
    
    DBG(3, ErrorF("xf86AiptekChangeControl changing to res %d\n",
              resolutions[0]));

    /* We don't know how to write, yet
     *
     * sprintf(str, "SU%d\r", resolutions[0]);
     * SYSCALL(write(local->fd, str, strlen(str)));
     */

    return(Success);
}

/*
 * xf86AiptekSwitchMode
 * Switches the mode. For now just absolute or relative, hopefully
 * more on the way.
 */
static int
xf86AiptekSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
{
    LocalDevicePtr  local  = (LocalDevicePtr)dev->public.devicePrivate;
    AiptekDevicePtr device = (AiptekDevicePtr)(local->private);

    DBG(3, ErrorF("xf86AiptekSwitchMode() dev=%p mode=%d\n", dev, mode));

    switch(mode)
    {
        case Absolute:
        {
            device->flags |= ABSOLUTE_FLAG;
        }
        break;

        case Relative:
        {
            device->flags &= ~ABSOLUTE_FLAG;
        }
        break;

        default:
        {
            DBG(1, ErrorF("xf86AiptekSwitchMode dev=%p invalid mode=%d\n",
                   dev, mode));
            return BadMatch;
        }
        break;
    }

    return Success;
}

/*
 * xf86AiptekAllocate
 * Allocates the device structures for the Aiptek.
 */
static LocalDevicePtr
xf86AiptekAllocate(char* name,
                int   flag)
{
    LocalDevicePtr    local;
    LocalDevicePtr*   deviceArray;
    AiptekDevicePtr   device;
    AiptekCommonPtr   common;

    DBG(3, ErrorF("xf86AiptekAllocate, with %s and %d\n", name, flag));

    device = (AiptekDevicePtr) xalloc(sizeof(AiptekDeviceRec));
    if (!device)
    {
        DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'device'\n"));
        return NULL;
    }

    common = (AiptekCommonPtr) xalloc(sizeof(AiptekCommonRec));
    if (!common)
    {
        DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'common'\n"));
        xfree(device);
        return NULL;
    }

    deviceArray = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr));
    if (!deviceArray)
    {
        DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'deviceArray'\n"));
        xfree(device);
        xfree(common);
        return NULL;
    }


    local = xf86AllocateInput(aiptekDrv, 0);
    if (!local)
    {
        DBG(3, ErrorF("xf86AiptekAllocate failed at xf86AllocateInput()\n"));
        xfree(device);
        xfree(common);
        xfree(deviceArray);
        return NULL;
    }

    local->name =                       name;
    local->type_name =                  "Aiptek";
    local->flags =                      0;
    local->device_control =             xf86AiptekProc;
    local->read_input =                 xf86AiptekHIDReadInput;
    local->control_proc =               xf86AiptekChangeControl;
    local->close_proc =                 xf86AiptekClose;
    local->switch_mode =                xf86AiptekSwitchMode;
    local->conversion_proc =            xf86AiptekConvert;
    local->reverse_conversion_proc =    xf86AiptekReverseConvert;

    local->fd =             VALUE_NA;
    local->atom =           0;
    local->dev =            NULL;
    local->private =        device;
    local->private_flags =  0;
    local->history_size  =  0;

    device->flags =         flag;       /* various flags (device type, 
                                         * coordinate type */
    device->xSize =         VALUE_NA; /* Active Area X */
    device->ySize =         VALUE_NA; /* Active Area Y */
    device->xOffset =       VALUE_NA; /* Active area offset X */
    device->yOffset =       VALUE_NA; /* Active area offset Y */
    device->xMax =          VALUE_NA; /* Max allowed X value */
    device->yMax =          VALUE_NA; /* Max allowed Y value */
    device->zMin =          VALUE_NA; /* Min allowed Z value */
    device->zMax =          VALUE_NA; /* Max allowed Z value */
    device->xTop =          VALUE_NA; /* Upper Left X coordinate */
    device->yTop =          VALUE_NA; /* Upper Left Y coordinate */
    device->xBottom =       VALUE_NA; /* Lower Right X coordinate */
    device->yBottom =       VALUE_NA; /* Lower Right Y coordinate */
    device->xThreshold =    VALUE_NA; /* X delta must be greater than */
    device->yThreshold =    VALUE_NA; /* Y delta must be greater than */
    device->zThreshold =    VALUE_NA; /* Z delta must be greater than */
    device->xTiltThreshold =VALUE_NA;
    device->yTiltThreshold =VALUE_NA;
    device->zMode =         VALUE_NA; /* Z: linear, soft, hard log */

    device->initNumber =    VALUE_NA; /* avoid re-init devices */
    device->screenNo =      VALUE_NA; /* Attached to X screen */

    device->common =        common;   /* Common info pointer */

    /* Record of the event currently being read of the queue */
    common->currentValues.eventType = 0;   /* Device event is for, e.g., */
                                            /* STYLUS, RUBBER, CURSOR */
    common->currentValues.x         = 0;   /* X coordinate */
    common->currentValues.y         = 0;   /* Y coordinate */
    common->currentValues.z         = 0;   /* Z (pressure) */
    common->currentValues.xTilt     = 0;   /* XTilt */
    common->currentValues.yTilt     = 0;   /* YTilt */
    common->currentValues.proximity = 0;   /* proximity bit */
    common->currentValues.macroKey  = VALUE_NA;   /* tablet macro key code */
    common->currentValues.button    = 0;   /* bitmask of buttons pressed */
    common->currentValues.distance  = 0;   /* currently unsupported */
    common->currentValues.wheel     = 0;   /* likewise */

    /* Record of the event previously read off of the queue */
    common->previousValues.eventType = 0;  /* Same comments as above */
    common->previousValues.x         = 0;
    common->previousValues.y         = 0;
    common->previousValues.z         = 0;
    common->previousValues.xTilt     = 0;
    common->previousValues.yTilt     = 0;
    common->previousValues.proximity = 0;
    common->previousValues.macroKey  = VALUE_NA;
    common->previousValues.button    = 0;
    common->previousValues.distance  = 0;
    common->previousValues.wheel     = 0;

    common->deviceName =    "";             /* device file name */
    common->flags =         0;              /* various flags */
    common->deviceArray =   deviceArray;    /* Array of tablets */
    common->deviceArray[0]= local;          /* with local as element */
    common->numDevices =    1;              /* number of devices */

    common->xCapacity =     0;              /* tablet's max X value */
    common->yCapacity =     0;              /* tablet's max Y value */
    common->zCapacity =     0;              /* tablet's max Z value */
    common->open =          xf86AiptekOpen; /* Open function */

    return local;
}

/*
 * xf86AiptekAllocateStylus
 */
static LocalDevicePtr
xf86AiptekAllocateStylus(void)
{
    LocalDevicePtr local = xf86AiptekAllocate(XI_STYLUS, STYLUS_ID);
    
    if (local)
    {
        local->type_name = "Stylus";
    }
    return local;
}

/*
 * xf86AiptekAllocateCursor
 */
static LocalDevicePtr
xf86AiptekAllocateCursor(void)
{
    LocalDevicePtr local = xf86AiptekAllocate(XI_CURSOR, CURSOR_ID);
    
    if (local)
    {
        local->type_name = "Cursor";
    }
    return local;
}

/*
 * xf86AiptekAllocateEraser
 */
static LocalDevicePtr
xf86AiptekAllocateEraser(void)
{
    LocalDevicePtr local = xf86AiptekAllocate(XI_ERASER,
            ABSOLUTE_FLAG|ERASER_ID);
    
    if (local)
    {
        local->type_name = "Eraser";
    }
    return local;
}

/*
 * Stylus device association
 */
DeviceAssocRec aiptek_stylus_assoc =
{
    STYLUS_SECTION_NAME,        /* config_section_name */
    xf86AiptekAllocateStylus    /* device_allocate */
};

/*
 * Cursor device association
 */
DeviceAssocRec aiptek_cursor_assoc =
{
    CURSOR_SECTION_NAME,        /* config_section_name */
    xf86AiptekAllocateCursor    /* device_allocate */
};

/*
 * Eraser device association
 */
DeviceAssocRec aiptek_eraser_assoc =
{
    ERASER_SECTION_NAME,        /* config_section_name */
    xf86AiptekAllocateEraser    /* device_allocate */
};

/*
 * xf86AiptekUninit --
 *
 * called when the driver is unloaded.
 */
static void
xf86AiptekUninit(InputDriverPtr    drv,
                 LocalDevicePtr    local,
                 int               flags)
{
    AiptekDevicePtr device = (AiptekDevicePtr) local->private;

    DBG(1, ErrorF("xf86AiptekUninit\n"));

    xf86AiptekProc(local->dev, DEVICE_OFF);

    if (device->common && device->common->xCapacity != -10101)
    {
        device->common->xCapacity = -10101;
        xfree(device->common);
    }
    xfree (device);
    xf86DeleteInput(local, 0);
}

/*
 * xf86AiptekInit --
 *
 * Called when the module subsection is found in XF86Config
 */
static InputInfoPtr
xf86AiptekInit(InputDriverPtr    drv,
               IDevPtr           dev,
               int               flags)
{
    LocalDevicePtr    local     = NULL;
    LocalDevicePtr    fakeLocal = NULL;
    AiptekDevicePtr   device    = NULL;
    AiptekCommonPtr   common    = NULL;
    LocalDevicePtr    locals;
    char*             s;
    int               shared;

    aiptekDrv = drv;

    xf86Msg(X_INFO, "xf86AiptekInit(): begins\n");

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

    fakeLocal->conf_idev = dev;

    /*
     * fakeLocal is here so it can have default serial init values.
     * Is this something to remove? TODO
     */
    xf86CollectInputOptions(fakeLocal, default_options, NULL);

/* Type */
    s = xf86FindOptionValue(fakeLocal->options, "Type");
    if (s && (xf86NameCmp(s, "stylus") == 0))
    {
        local = xf86AiptekAllocateStylus();
    }
    else if (s && (xf86NameCmp(s, "cursor") == 0))
    {
        local = xf86AiptekAllocateCursor();
    }
    else if (s && (xf86NameCmp(s, "eraser") == 0))
    {
        local = xf86AiptekAllocateEraser();
    }
    else
    {
        xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
                  "Must be one of 'stylus', 'cursor', or 'eraser'\n",
                  dev->identifier);
    }

    if(!local)
    {
        xfree(fakeLocal);
        return NULL;
    }

    device = (AiptekDevicePtr) local->private;

    common              = device->common;

    local->options      = fakeLocal->options;
    local->conf_idev    = fakeLocal->conf_idev;
    local->name         = dev->identifier;
    xfree(fakeLocal);

/* Device */
/* (mandatory) */
    common->deviceName = xf86FindOptionValue(local->options, "Device");
    if(!common->deviceName)
    {
        xf86Msg(X_ERROR, "%s: No Device specified.\n", dev->identifier);
        goto SetupProc_fail;
    }

    /*
     * Lookup to see if there is another aiptek device sharing the
     * same device.
     */

    shared = 0;
    for (locals = xf86FirstLocalDevice();
         locals != NULL;
         locals = locals->next)
    {
        if((local != locals) &&
           (locals->device_control == xf86AiptekProc) &&
           (strcmp(((AiptekDevicePtr)locals->private)->common->deviceName,
                    common->deviceName) == 0))
        {
            xf86Msg(X_CONFIG,
                    "xf86AiptekConfig: device shared between %s and %s\n",
                    local->name,
                    locals->name);

            shared = 1;

            xfree(common->deviceArray);
            xfree(common);

            common = device->common =
                ((AiptekDevicePtr) locals->private)->common;
            common->numDevices++;
            common->deviceArray = (LocalDevicePtr*)xrealloc(common->deviceArray,
                    sizeof(LocalDevicePtr)*common->numDevices);
            common->deviceArray[common->numDevices-1] = local;
            break;
        }
        else
        {
            xf86Msg(X_CONFIG, 
                    "xf86AiptekConfig: device not shared btw %s and %s\n",
                    local->name, locals->name);
        }
    }

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

    /* If this is the first device using the aiptek driver, let's open
     * the interface so as to obtain legit values for xCapacity and yCapacity
     * (and then quickly close it). Yes, the word for this is "kludge".
     * I am sufficiently ashamed :-) TODO kludge alert!
     */
    if ( shared == 0)
    {
        xf86AiptekHIDOpen(local);
        close(local->fd);
        local->fd=-1;
    }

    /* Optional configuration */
    xf86Msg(X_CONFIG, "%s device is %s\n", dev->identifier,
            common->deviceName);

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

/* zMode */
    s = xf86FindOptionValue(local->options, "Pressure");
    if ( s && (xf86NameCmp(s, "hard") == 0))
    {
        device->zMode = PRESSURE_MODE_HARD_SMOOTH;
    }
    else if ( s && (xf86NameCmp(s, "soft") == 0))
    {
        device->zMode = PRESSURE_MODE_SOFT_SMOOTH;
    }
    else if (s && (xf86NameCmp(s, "normal") == 0))
    {
        device->zMode = PRESSURE_MODE_LINEAR;
    }
    else if (s)
    {
        xf86Msg(X_ERROR, "%s: invalid Mode ('normal', 'soft' or 'hard').\n",
            dev->identifier);
    }

/* Mode */
    s = xf86FindOptionValue(local->options, "Mode");
    if (s && (xf86NameCmp(s, "absolute") == 0))
    {
        device->flags |= ABSOLUTE_FLAG;
    }
    else if (s && (xf86NameCmp(s, "relative") == 0)) 
    {
        device->flags &= ~ABSOLUTE_FLAG;
    }
    else if (s)
    {
        xf86Msg(X_ERROR, "%s: invalid Mode ('absolute' or 'relative').\n",
            dev->identifier);
        device->flags |= ABSOLUTE_FLAG;
    }
    xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name,
        (device->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");

#ifdef LINUX_INPUT
    /* The define-name is accurate; the XFree86 keyword is not. We are
     * reading from a Linux kernel "Input" device. The Input device
     * layer generally supports mice, joysticks, and keyboards. As
     * an extension, the Input device layer also supports HID devices.
     * HID is a standard specified by the USB Implementors Forum. Ergo,
     * 99.9% of HID devices are USB devices.
     *
     * This option is misnamed, misunderstood, misanthrope. Maybe.
     */
    if (xf86SetBoolOption(local->options, "USB",
                          (common->open == xf86AiptekHIDOpen)))
    {
        local->read_input=xf86AiptekHIDReadInput;
        common->open=xf86AiptekHIDOpen;
        xf86Msg(X_CONFIG, "%s: reading USB link\n", dev->identifier);
    }
#else
    if (xf86SetBoolOption(local->options, "USB", 0))
    {
        ErrorF("The Aiptek USB driver isn't available for your platform.\n");
        goto SetupProc_fail;
    }
#endif

/* ScreenNo */
    device->screenNo = xf86SetIntOption(local->options, "ScreenNo", VALUE_NA);
    if (device->screenNo != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: attached to screen number %d\n",
                dev->identifier, device->screenNo);
    }

/* KeepShape */
    if (xf86SetBoolOption(local->options, "KeepShape", 0))
    {
        device->flags |= KEEP_SHAPE_FLAG;
        xf86Msg(X_CONFIG, "%s: keeps shape\n", dev->identifier);
    }

/* XSize */
    device->xSize = xf86SetIntOption(local->options, "XSize", device->xSize);
    device->xSize = xf86SetIntOption(local->options, "SizeX", device->xSize);
    if (device->xSize != VALUE_NA) 
    {
        xf86Msg(X_CONFIG, "%s: XSize/SizeX = %d\n", dev->identifier,
                device->xSize);
    }

/* YSize */
    device->ySize = xf86SetIntOption(local->options, "YSize", device->ySize);
    device->ySize = xf86SetIntOption(local->options, "SizeY", device->ySize);
    if (device->ySize != VALUE_NA) 
    {
        xf86Msg(X_CONFIG, "%s: YSize/SizeY = %d\n", dev->identifier,
                device->ySize);
    }

/* XOffset */
    device->xOffset = xf86SetIntOption(local->options, "XOffset", 
            device->xOffset);
    device->xOffset = xf86SetIntOption(local->options, "OffsetX", 
            device->xOffset);
    if (device->xOffset != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: XOffset/OffsetX = %d\n", dev->identifier,
                device->xOffset);
    }

/* YOffset */
    device->yOffset = xf86SetIntOption(local->options, "YOffset",
            device->yOffset);
    device->yOffset = xf86SetIntOption(local->options, "OffsetY",
            device->yOffset);
    if (device->yOffset != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: YOffset/OffsetY = %d\n", dev->identifier,
                device->yOffset);
    }

/* XThreshold */
    device->xThreshold = xf86SetIntOption(local->options, "XThreshold",
            device->xThreshold);
    device->xThreshold = xf86SetIntOption(local->options, "ThresholdX",
            device->xThreshold);
    if (device->xThreshold != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: XThreshold/ThresholdX = %d\n",
                dev->identifier, device->xThreshold);
    }

/* YThreshold */
    device->yThreshold = xf86SetIntOption(local->options, "YThreshold",
            device->yThreshold);
    device->yThreshold = xf86SetIntOption(local->options, "ThresholdY",
            device->yThreshold);
    if (device->yThreshold != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: YThreshold/ThresholdY = %d\n",
                dev->identifier, device->yThreshold);
    }

/* ZThreshold */
    device->zThreshold = xf86SetIntOption(local->options, "ZThreshold",
            device->zThreshold);
    device->zThreshold = xf86SetIntOption(local->options, "ThresholdZ",
            device->zThreshold);
    if (device->zThreshold != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: ZThreshold/ThresholdZ = %d\n",
                dev->identifier, device->zThreshold);
    }

/* XTiltThreshold */
    device->xTiltThreshold = xf86SetIntOption(local->options, "XTiltThreshold",
            device->xTiltThreshold);
    device->xTiltThreshold = xf86SetIntOption(local->options, "ThresholdXTilt",
            device->xTiltThreshold);
    if (device->xTiltThreshold != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: XTiltThreshold = %d\n",
                dev->identifier, device->xTiltThreshold);
    }

/* YTiltThreshold */
    device->yTiltThreshold = xf86SetIntOption(local->options, "YTiltThreshold",
            device->yTiltThreshold);
    device->yTiltThreshold = xf86SetIntOption(local->options, "ThresholdYTilt",
            device->yTiltThreshold);
    if (device->yTiltThreshold != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: YTiltThreshold = %d\n",
                dev->identifier, device->yTiltThreshold);
    }

/* XMax */
    device->xMax = xf86SetIntOption(local->options, "XMax", device->xMax);
    device->xMax = xf86SetIntOption(local->options, "MaxX", device->xMax);
    if (device->xMax != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: XMax/MaxX = %d\n", dev->identifier,
                device->xMax);
    }

/* YMax */
    device->yMax = xf86SetIntOption(local->options, "YMax", device->yMax);
    device->yMax = xf86SetIntOption(local->options, "MaxY", device->yMax);
    if (device->yMax != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: YMax/MaxY = %d\n", dev->identifier,
                device->yMax);
    }

/* ZMax */
    device->zMax = xf86SetIntOption(local->options, "ZMax", device->zMax);
    device->zMax = xf86SetIntOption(local->options, "MaxZ", device->zMax);
    if (device->zMax != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: ZMax/MaxZ = %d\n", dev->identifier,
                device->zMax);
    }

/* ZMin */
    device->zMin = xf86SetIntOption(local->options, "ZMin", device->zMin);
    device->zMin = xf86SetIntOption(local->options, "MinZ", device->zMin);
    if (device->zMin != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: ZMin/MinZ = %d\n", dev->identifier,
                device->zMin);
    }

/* TopX */
    device->xTop = xf86SetIntOption(local->options, "TopX", device->xTop);
    device->xTop = xf86SetIntOption(local->options, "XTop", device->xTop);
    if (device->xTop != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: TopX/XTop = %d\n", dev->identifier,
                device->xTop);
    }

/* TopY */
    device->yTop = xf86SetIntOption(local->options, "TopY", device->yTop);
    device->yTop = xf86SetIntOption(local->options, "YTop", device->yTop);
    if (device->yTop != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: TopY/YTop = %d\n", dev->identifier,
                device->yTop);
    }

/* BottomX */
    device->xBottom = xf86SetIntOption(local->options, "BottomX",
            device->xBottom);
    device->xBottom = xf86SetIntOption(local->options, "XBottom",
            device->xBottom);
    if (device->xBottom != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: BottomX/XBottom = %d\n", dev->identifier,
                device->xBottom);
    }

/* BottomY */
    device->yBottom = xf86SetIntOption(local->options, "BottomY",
            device->yBottom);
    device->yBottom = xf86SetIntOption(local->options, "YBottom",
            device->yBottom);
    if (device->yBottom != VALUE_NA)
    {
        xf86Msg(X_CONFIG, "%s: BottomY/YBottom = %d\n", dev->identifier,
                device->yBottom);
    }

/* InvX */
    if (xf86SetBoolOption(local->options, "InvX", FALSE))
    {
        device->flags |= INVX_FLAG;
        xf86Msg(X_CONFIG, "%s: InvX\n", dev->identifier);
    }

/* InvY */
    if (xf86SetBoolOption(local->options, "InvY", FALSE))
    {
        device->flags |= INVY_FLAG;
        xf86Msg(X_CONFIG, "%s: InvY\n", dev->identifier);
    }

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

        switch(val)
        {
            case 19200:
                break;
            case 9600:
                break;
            default:
                xf86Msg(X_ERROR, "%s: Illegal BaudRate (9600 or 19200).",
                        dev->identifier);
                break;
        }
        if (xf86Verbose)
        {
            xf86Msg(X_CONFIG, "%s: BaudRate %u\n", dev->identifier,
                    val);
        }
    }
    xf86Msg(X_CONFIG, "%s: xf86AiptekInit() finished\n", dev->identifier);

    /* Mark the device as configured */
    local->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;

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

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

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

/*
 * xf86AiptekPlug --
 *
 * called when the module subsection is found in XF86Config
 */
static pointer
xf86AiptekPlug(pointer    module,
               pointer    options,
               int*       errmaj,
               int*       errmin)
{
    DBG(1, ErrorF("xf86AiptekPlug\n"));

    xf86AddInputDriver(&AIPTEK, module, 0);

    return module;
}

static XF86ModuleVersionInfo xf86AiptekVersionRec =
{
    "aiptek",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    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 aiptekModuleData =
{
    &xf86AiptekVersionRec,
    xf86AiptekPlug,
    xf86AiptekUnplug
};

#endif /* XFree86LOADER */

/* end of xf86Aiptek.c */