Edit

IABSD.fr/xenocara/driver/xf86-input-hyperpen/src/xf86HyperPen.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2011-11-05 13:49:26
    Hash : b5ad791d
    Message : Update to xf86-input-hyperpen 1.4.1

  • driver/xf86-input-hyperpen/src/xf86HyperPen.c
  • // -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
    /*
     * xf86HyperPen
     *
     * Based on the xf86Summa driver.
     *
     * Modified for the Aiptek HyperPen 6000 / Tevion MD 9310
     *   (c) 2000 Roland Jansen <roland@lut.rwth-aachen.de>
     *
     *
     *
     * added button and 19200 bps stuff from the DigitalEdge driver on sourceforge
     *   (c) 2000 Christian Herzog <daduke@dataway.ch>
     *
     *
     */
    
    /* xf86Summa
     * Copyright 1996 by Steven Lang <tiger@tyger.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 Steven Lang not be used in advertising
     * or publicity pertaining to distribution of the software without specific,
     * written prior permission.  Steven Lang makes no representations about the
     * suitability of this software for any purpose.  It is provided "as is"
     * without express or implied warranty.
     *
     * STEVEN LANG DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     * EVENT SHALL STEVEN LANG 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.
     */
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <xorgVersion.h>
    
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <termios.h>
    
    #include <misc.h>
    #include <xf86.h>
    #define NEED_XF86_TYPES
    #if !defined(DGUX)
    #include <xisb.h>
    #endif
    #include <xf86_OSproc.h>
    #include <xf86Xinput.h>
    #include <exevents.h>        /* Needed for InitValuator/Proximity stuff */
    #include <X11/keysym.h>
    #include <mipointer.h>
    #include <xf86Module.h>
    
    /*
     * Be sure to set vmin appropriately for your device's protocol. You want to
     * read a full packet before returning
     */
    
    static char *default_options[] = {
        "BaudRate",    "9600",
        "DataBits",    "8",
        "StopBits",    "1",
        "Parity",      "Odd",
        "FlowControl", "Xoff",
        "VTime",       "10",
        "VMin",        "7",
        NULL
    };
    
    
    
    /*
    ** Debugging macros
    */
    #ifdef DBG
    #undef DBG
    #endif
    #ifdef DEBUG
    #undef DEBUG
    #endif
    
    #ifndef INI_DEBUG_LEVEL
    #define INI_DEBUG_LEVEL 0
    #endif
    
    static int debug_level = INI_DEBUG_LEVEL;
    #define DEBUG 1
    #if DEBUG
    #define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
    #else
    #define DBG(lvl, f)
    #endif
    
    /*
    ** Device records
    */
    #define ABSOLUTE_FLAG   1
    #define STYLUS_FLAG     2
    #define INVX_FLAG       4
    #define INVY_FLAG       8
    #define BAUD_19200_FLAG 16
    
    int stylus;
    
    typedef struct {
        char *hypDevice;          /* device file name */
        int hypButTrans;          /* button translation flags */
        int hypOldX;              /* previous X position */
        int hypOldY;              /* previous Y position */
        int hypOldZ;              /* previous Z position */
        int hypOldProximity;      /* previous proximity */
        int hypOldPush;           /* previous buttons state */
        int hypOldButtons;        /* previous buttons state */
        int hypOldBarrel;         /* previous buttons state */
        int hypOldBarrel1;        /* previous buttons state */
        int hypOldPressure;       /* previous pen pressure */
        int hypMaxX;              /* max X value */
        int hypMaxY;              /* max Y value */
        int hypMaxZ;              /* max Z value */
        int hypXSize;             /* active area X size */
        int hypXOffset;           /* active area X offset */
        int hypYSize;             /* active area Y size */
        int hypYOffset;           /* active area Y offset */
        int hypRes;               /* resolution in lines per inch */
        int flags;                /* various flags */
        int hypIndex;             /* number of bytes read */
        int modelid;              /* model id */
        int PT;                   /* pressure threshold */
        int AutoPT;               /* automatically set PT*/
        int PMax;                 /* maximum pressure read from tablet */
        unsigned char hypData[7]; /* data read on the device */
    } HyperPenDeviceRec, *HyperPenDevicePtr;
    
    /*
    ** List of model IDs
    */
    static struct MODEL_ID {
        unsigned id;
        char* name;
    } models[] = {
        {0x32, "HyperPen 3000"},
        {0x43, "HyperPen 4000"},
        {0x54, "HyperPen 5000"},
        {0x64, "HyperPen 6000"},
        {0, NULL}
    };
    
    /*
    ** Contants and macro
    */
    #define BUFFER_SIZE    256      /* size of reception buffer */
    
    #define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
    
    
    #define SS_RESET       "\0"   /* Reset */
    #define SS_GETID       "\5"   /* Get Model ID */
    #define SS_CONFIG      "a"    /* Send configuration (max coords) */
    #define SS_PROMPT_MODE "D"    /* Prompt mode */
    #define SS_STREAM_MODE "@"    /* Stream mode */
    #define SS_RATE        "\xb5" /* 19200 bps */
    #define SS_ABSOLUTE    "F"    /* absolute mode */
    #define SS_RELATIVE    "E"    /* relative mode */
    #define SS_MACROKEY    "U"    /* enable F-keys */
    #define SS_MACRO_4K    "\2"   /* start F-keys for Hyperpen 4000 */
    #define SS_MACRO_56K   "\1"   /* start F-keys for Hyperpen 5000/6000 */
    static const char * ss_initstr = SS_STREAM_MODE;
    
    #define PHASING_BIT   0x80
    #define PROXIMITY_BIT 0x40
    #define XSIGN_BIT     0x10
    #define YSIGN_BIT     0x08
    #define BUTTON_BITS   0x07
    #define COORD_BITS    0x7f
    #define XAXIS_BITS    0x03
    #define YAXIS_BITS    0x0C
    #define ZMASK_BIT     0x70
    #define TIP_BITS      0x01
    #define F_BIT         0x20
    
    
    /* macro from counts/inch to counts/meter */
    #define LPI2CPM(res) (res * 1000 / 25.4)
    
    /*
    ** xf86HypReadInput
    ** Reads from the HyperPen and posts any new events to the server.
    */
    static void
    xf86HypReadInput(InputInfoPtr pInfo)
    {
        HyperPenDevicePtr priv = (HyperPenDevicePtr) pInfo->private;
        int len, loop;
        int is_absolute;
        int f_keys, f_key, tip;
        int x, y, bx, by, barrel, barrel1, prox, pressure, button, push;
        int hw_pressure;
    
        DeviceIntPtr    device;
        unsigned char    buffer[BUFFER_SIZE];
    
        SYSCALL(len = read(pInfo->fd, buffer, sizeof(buffer)));
    
        if (len <= 0) {
            Error("error reading HyperPen device");
            return;
        }
    
        for(loop=0; loop<len; loop++) {
            if ((priv->hypIndex == 0) && !(buffer[loop] & PHASING_BIT)) {
                /* magic bit is not OK */
                DBG(6, ErrorF("xf86HypReadInput bad magic number 0x%x\n", buffer[loop]));;
                continue;
            }
    
            priv->hypData[priv->hypIndex++] = buffer[loop];
    
            if (priv->hypIndex == (priv->flags & ABSOLUTE_FLAG? 7 : 5)) {
                /* the packet is OK */
                /* reset char count for next read */
                priv->hypIndex = 0;
    
                prox = (priv->hypData[0] & PROXIMITY_BIT)? 0: 1;
                tip = (priv->hypData[0] & TIP_BITS)? 1:0;
                button = (priv->hypData[0] & BUTTON_BITS);
    
                f_keys = (priv->hypData[0] & F_BIT);
                pressure = (int) priv->hypData[6] + (((int) priv->hypData[5] & ZMASK_BIT) << 3);
    
                if ((tip==0) && (button==0) && (pressure>2) && (pressure != 1022)) {
                    priv->flags |= STYLUS_FLAG; stylus=1;
                } else if ((tip==0) && (button==0) && (pressure==0)) {
                    priv->flags &= ~STYLUS_FLAG; stylus=0; pressure = 1019;
                }
    
                is_absolute = stylus;
    
                x = priv->hypData[1] + (priv->hypData[2] << 7) + ((priv->hypData[5] & XAXIS_BITS) << 14);
                y = priv->hypData[3] + (priv->hypData[4] << 7) + ((priv->hypData[5] & YAXIS_BITS) << 12);
    
                if ((f_keys) && (tip)) (f_key = ((x >> 7) + 1) >> 1); else f_key =0;
    
                x -= priv->hypXOffset;
                y = priv->hypYSize - y + priv->hypYOffset;
                if (x < 0) x = 0;
                if (y < 0) y = 0;
                if (x > priv->hypXSize) x = priv->hypXSize;
                if (y > priv->hypYSize) y = priv->hypYSize;
                bx=x;
                by=y;
    
                if (!is_absolute) {
                    x -= priv->hypOldX;
                    y -= priv->hypOldY;
                }
    
                hw_pressure=pressure;
    
                if (!priv->PMax) priv->PMax=1000;
                if (pressure>1020) pressure=priv->PT;
                if (priv->AutoPT) {
                    if ((pressure>1) && !(tip)) priv->PT = pressure;
                    pressure = 511 * (pressure - priv->PT - 10) / (priv->PMax - priv->PT);
                } else {
                    pressure = 511 * (pressure - priv->PT) / (priv->PMax - priv->PT);
                    button &= ~1;
                    if (pressure>0) button |=1;
                };
    
                if (pressure > 511) pressure = 511;
                if (pressure < 0) pressure = 0;
    
                push = button & 1;
                barrel = button & 2;
                barrel1 = button & 4;
    
                DBG(6, ErrorF("hw-press=%d\ttip=%d\tbarrel=%d\tbarrel1=%d\tpush=%d\tpressure=%d\tPT=%d\tbuttons=%d\tf-key=%d\n",hw_pressure, priv->hypData[0] & TIP_BITS, barrel, barrel1, push, pressure, priv->PT, priv->hypData[0] & BUTTON_BITS,f_key));
    
                device = pInfo->dev;
    
                /* coordonates are ready we can send events */
    
                if ((prox) && !(f_keys)) {
                    if (!(priv->hypOldProximity))
                        xf86PostProximityEvent(device, 1, 0, 3, x, y, pressure);
    
                    if ((is_absolute && ((priv->hypOldX != x) ||
                                         (priv->hypOldY != y) ||
                                         (priv->hypOldZ != pressure)))
                        || (!is_absolute && (x || y))) {
                        if (is_absolute || priv->hypOldProximity) {
                            xf86PostMotionEvent(device, is_absolute, 0, 3, x, y, pressure);
                        }
                    }
    
    
    
                    if (priv->hypOldBarrel1 != barrel1) {
                        int delta;
                        delta = barrel1 - priv->hypOldBarrel1;
                        if (priv->hypOldBarrel1 != barrel1) {
    
    
    
                            xf86PostButtonEvent(device, is_absolute, 2, (delta > 0), 0, 3, x, y, pressure);
                        }
                    }
    
                    if (priv->hypOldBarrel != barrel) {
                        int delta;
                        delta = barrel - priv->hypOldBarrel;
                        if (priv->hypOldBarrel != barrel) {
    
                            xf86PostButtonEvent(device, is_absolute, 3, (delta > 0), 0, 3, x, y, pressure);
                        }
                    }
    
                    if ((priv->hypOldPush != push) && !barrel && !barrel1) {
                        int delta;
                        delta = push - priv->hypOldPush;
                        if (priv->hypOldPush != push) {
    
                            xf86PostButtonEvent(device, is_absolute, 1, (delta > 0), 0, 3, x, y, pressure);
                        }
                    }
    
                    priv->hypOldX = bx;
                    priv->hypOldY = by;
                    priv->hypOldPush = push;
                    priv->hypOldBarrel = barrel;
                    priv->hypOldBarrel1 = barrel1;
                    priv->hypOldProximity = prox;
    
                } else { /* !PROXIMITY */
                    /* Any changes in buttons are ignored when !proximity */
                    if (priv->hypOldProximity)
                        xf86PostProximityEvent(device, 0, 0, 3, x, y, pressure);
                    priv->hypOldProximity = 0;
                }
            }
        }
    }
    
    /*
    ** xf86HypControlProc
    ** It really does do something. Honest!
    */
    static void
    xf86HypControlProc(DeviceIntPtr device, PtrCtrl *ctrl)
    {
        DBG(2, ErrorF("xf86HypControlProc\n"));
    }
    
    /*
    ** xf86HypWriteAndRead
    ** Write data, and get the response.
    */
    static char *
    xf86HypWriteAndRead(int fd, char *data, char *buffer, int len, int cr_term)
    {
        int err, numread = 0;
    
        SYSCALL(err = write(fd, data, strlen(data)));
        if (err == -1) {
            Error("HyperPen write");
            return NULL;
        }
    
        while (numread < len) {
            err = xf86WaitForInput(fd, 100000);
            if (err == -1) {
                Error("HyperPen select");
                return NULL;
            }
            if (!err) {
                ErrorF("Timeout while reading HyperPen tablet. No tablet connected ???\n");
                return NULL;
            }
    
            SYSCALL(err = read(fd, buffer + numread++, 1));
            if (err == -1) {
                Error("HyperPen read");
                return NULL;
            }
            if (!err) {
                --numread;
                break;
            }
            if (cr_term && buffer[numread - 1] == '\r') {
                buffer[numread - 1] = 0;
                break;
            }
        }
        buffer[numread] = 0;
        return buffer;
    }
    
    /*
    ** xf86HypOpen
    ** Open and initialize the tablet, as well as probe for any needed data.
    */
    
    #define WAIT(t)                                                     \
        err = xf86WaitForInput(-1, ((t) * 1000));                       \
        if (err == -1) {                                                \
            ErrorF("HyperPen select error : %s\n", strerror(errno));    \
            return !Success;                                            \
        }
    
    static Bool
    xf86HypOpen(InputInfoPtr pInfo)
    {
        char        buffer[256];
        int            err, idx;
        int            i, n;
        double        res100;
        double        sratio, tratio;
        HyperPenDevicePtr    priv = (HyperPenDevicePtr)pInfo->private;
    
        DBG(1, ErrorF("opening %s\n", priv->hypDevice));
    
        pInfo->fd = xf86OpenSerial(pInfo->options);
        if (pInfo->fd == -1) {
            Error(priv->hypDevice);
            return !Success;
        }
        DBG(2, ErrorF("%s opened as fd %d\n", priv->hypDevice, pInfo->fd));
    
        if (xf86SetSerialSpeed(pInfo->fd, 9600) < 0)
            return !Success;
    
        DBG(1, ErrorF("initializing HyperPen tablet\n"));
    
        /* Send reset to the tablet */
    
        write(pInfo->fd, SS_RESET, strlen(SS_RESET));
        WAIT(1000);
    
        /* Put it in prompt mode so it doesn't say anything before we're ready */
        SYSCALL(err = write(pInfo->fd, SS_PROMPT_MODE, strlen(SS_PROMPT_MODE)));
        if (err == -1) {
            Error("HyperPen write");
            return !Success;
        }
        /* Clear any pending input */
        tcflush(pInfo->fd, TCIFLUSH);
    
        DBG(2, ErrorF("reading model\n"));
    
        if (!xf86HypWriteAndRead(pInfo->fd, SS_GETID, buffer, 1, 0))
            return !Success;
    
        priv->modelid=buffer[0];
    
        for (n = -1, i = 0; models[i].id != 0; i++)
            if (models[i].id == priv->modelid)
                n = i;
    
        xf86Msg(X_PROBED, " HyperPen Model: 0x%x (%s)\n",
                priv->modelid, n==-1? "UNKNOWN":models[n].name);
    
    
        /* enable F-Keys */
        SYSCALL(err = write(pInfo->fd, SS_MACROKEY, strlen(SS_MACROKEY)));
        if (err == -1) {
            ErrorF("HyperPen write error : %s\n", strerror(errno));
            return !Success;
        }
    
        DBG(6, ErrorF("prepared F-keys\n"));
    
        /* start sequence depends on model ID */
        if (priv->modelid == 0x43) SYSCALL(err = write(pInfo->fd, SS_MACRO_4K, strlen(SS_MACRO_4K))); else SYSCALL(err = write(pInfo->fd, SS_MACRO_56K, strlen(SS_MACRO_56K)));
    
        if (err == -1) {
            xf86Msg(X_ERROR, "HyperPen write error : %s\n", strerror(errno));
            return !Success;
        }
    
        DBG(6, ErrorF("started F-keys\n"));
    
        priv->hypRes = 500;
        res100 = priv->hypRes / 100;
    
        DBG(2, ErrorF("reading max coordinates\n"));
    
        if (!xf86HypWriteAndRead(pInfo->fd, SS_CONFIG, buffer, 5, 0))
            return !Success;
    
        priv->hypMaxX = (buffer[1] & 0x7f) | (buffer[2] << 7);
        priv->hypMaxY = (buffer[3] & 0x7f) | (buffer[4] << 7);
        priv->hypMaxZ = 512;
    
        xf86Msg(X_PROBED, "HyperPen max tablet size %d.%02dinx%d.%02din, %dx%d "
                   "lines of resolution\n",
                   priv->hypMaxX / priv->hypRes,
                   (priv->hypMaxX * 100 / priv->hypRes) % 100,
                   priv->hypMaxY / priv->hypRes,
                   (priv->hypMaxY * 100 / priv->hypRes) % 100,
                   priv->hypMaxX, priv->hypMaxY);
    
        if (priv->hypXOffset >= 0 && priv->hypYOffset >= 0) {
            priv->hypXOffset *= res100;
            priv->hypYOffset *= res100;
            priv->hypMaxX -= priv->hypXOffset;
            priv->hypMaxY -= priv->hypYOffset;
        }
    
        if (priv->hypXSize > 0 && priv->hypYSize > 0) {
            if ((priv->hypXSize * res100) <= priv->hypMaxX &&
                (priv->hypYSize * res100) <= priv->hypMaxY) {
                priv->hypXSize *= res100;
                priv->hypYSize *= res100;
            } else {
                xf86Msg(X_PROBED, "HyperPen active area bigger than tablet, "
                       "assuming maximum\n");
                priv->hypXSize = priv->hypMaxX;
                priv->hypYSize = priv->hypMaxY;
            }
        } else {
            priv->hypXSize = priv->hypMaxX;
            priv->hypYSize = priv->hypMaxY;
        }
    
        /* map tablet area by screen aspect ratio */
        sratio = (double)screenInfo.screens[0]->height /
            (double)screenInfo.screens[0]->width;
        tratio = (double)priv->hypMaxY / (double)priv->hypMaxX;
    
        if (tratio <= 1.0) {        /* tablet horizontal > vertical */
            priv->hypXSize = (double)priv->hypYSize / sratio;
            if (priv->hypXSize > priv->hypMaxX) priv->hypXSize = priv->hypMaxX;
        } else {
            priv->hypYSize = (double)priv->hypXSize / sratio;
            if (priv->hypYSize > priv->hypMaxY) priv->hypYSize = priv->hypMaxY;
        }
        xf86Msg(X_PROBED, "HyperPen using tablet area %d by %d, at res %d lpi\n",
               priv->hypXSize, priv->hypYSize, priv->hypRes);
    
        if (priv->flags & BAUD_19200_FLAG) {
            /* Send 19200 baud to the tablet */
            SYSCALL(err = write(pInfo->fd, SS_RATE, strlen(SS_RATE)));
            if (err == -1) {
                ErrorF("HyperPen write error : %s\n", strerror(errno));
                return !Success;
            }
    
            DBG(6, ErrorF("tablet set to 19200 bps\n"));
    
            /* Wait 10 mSecs */
            WAIT(10);
    
            /* Set the speed of the serial link to 19200 */
            if (xf86SetSerialSpeed(pInfo->fd, 19200) < 0) {
                return !Success;
            }
    
            DBG(6, ErrorF("set serial speed to 19200\n"));
        }
    
        /* Sets up the tablet mode to increment, stream, and such */
        for (idx = 0; ss_initstr[idx]; idx++) {
            buffer[idx] = ss_initstr[idx];
        }
    
        buffer[idx] = 0;
    
        SYSCALL(err = write(pInfo->fd, buffer, idx));
    
        if (err == -1) {
            Error("HyperPen write");
            return !Success;
        }
    
        if (err <= 0) {
            SYSCALL(close(pInfo->fd));
            return !Success;
        }
        return Success;
    }
    
    /*
    ** xf86HypOpenDevice
    ** Opens and initializes the device driver stuff or something.
    */
    static int
    xf86HypOpenDevice(DeviceIntPtr pHyp)
    {
        InputInfoPtr    pInfo = (InputInfoPtr)pHyp->public.devicePrivate;
        HyperPenDevicePtr priv = (HyperPenDevicePtr)(pInfo->private);
        Atom axis_labels[3] = { 0 };
    
        if (xf86HypOpen(pInfo) != Success) {
            if (pInfo->fd >= 0) {
                SYSCALL(close(pInfo->fd));
            }
            pInfo->fd = -1;
        }
    
        /* Set the real values */
        InitValuatorAxisStruct(pHyp,
                               0,
                               axis_labels[0],
                               0, /* min val */
                               priv->hypXSize, /* max val */
                               LPI2CPM(priv->hypRes), /* resolution */
                               0, /* min_res */
                               LPI2CPM(priv->hypRes), /* max_res */
                               (priv->flags & ABSOLUTE_FLAG)? Absolute : Relative);
        InitValuatorAxisStruct(pHyp,
                               1,
                               axis_labels[1],
                               0, /* min val */
                               priv->hypYSize, /* max val */
                               LPI2CPM(priv->hypRes), /* resolution */
                               0, /* min_res */
                               LPI2CPM(priv->hypRes), /* max_res */
                               (priv->flags & ABSOLUTE_FLAG)? Absolute : Relative);
        InitValuatorAxisStruct(pHyp,
                               2,
                               axis_labels[2],
                               0, /* min val */
                               511, /* max val */
                               512, /* resolution */
                               0, /* min_res */
                               512,  /* max_res */
                               (priv->flags & ABSOLUTE_FLAG)? Absolute : Relative);
        return (pInfo->fd != -1);
    }
    
    /*
    ** xf86HypProc
    ** Handle requests to do stuff to the driver.
    */
    static int
    xf86HypProc(DeviceIntPtr pHyp, int what)
    {
        CARD8        map[25];
        int            nbaxes;
        int            nbbuttons;
        int            loop;
        InputInfoPtr    pInfo = (InputInfoPtr)pHyp->public.devicePrivate;
        HyperPenDevicePtr priv = (HyperPenDevicePtr)(pInfo->private);
        Atom btn_labels[4] = { 0 };
        Atom axis_labels[3] = { 0 };
    
        switch (what) {
        case DEVICE_INIT:
            DBG(1, ErrorF("xf86HypProc pHyp=%p what=INIT\n", (void *)pHyp));
    
            nbaxes = 3;            /* X, Y, Z */
            nbbuttons = (priv->flags & STYLUS_FLAG)? 3 : 4;
    
            for(loop=1; loop<=nbbuttons; loop++) map[loop] = loop;
    
            if (InitButtonClassDeviceStruct(pHyp,
                                            nbbuttons,
                                            btn_labels,
                                            map) == FALSE) {
                ErrorF("unable to allocate Button class device\n");
                return !Success;
            }
    
            if (InitFocusClassDeviceStruct(pHyp) == FALSE) {
                ErrorF("unable to init Focus class device\n");
                return !Success;
            }
    
            if (InitPtrFeedbackClassDeviceStruct(pHyp,
                                                 xf86HypControlProc) == FALSE) {
                ErrorF("unable to init ptr feedback\n");
                return !Success;
            }
    
            if (InitProximityClassDeviceStruct(pHyp) == FALSE) {
                ErrorF("unable to init proximity class device\n");
                return !Success;
            }
    
            if (InitValuatorClassDeviceStruct(pHyp,
                                              nbaxes,
                                              axis_labels,
                                              GetMotionHistorySize(),
                                              (priv->flags & ABSOLUTE_FLAG)? Absolute: Relative)
                == FALSE) {
                ErrorF("unable to allocate Valuator class device\n");
                return !Success;
            }
            /* allocate the motion history buffer if needed */
            xf86MotionHistoryAllocate(pInfo);
            /* open the device to gather informations */
            xf86HypOpenDevice(pHyp);
            break;
    
        case DEVICE_ON:
            DBG(1, ErrorF("xf86HypProc pHyp=%p what=ON\n", (void *)pHyp));
    
            if ((pInfo->fd < 0) && (!xf86HypOpenDevice(pHyp))) {
                return !Success;
            }
            xf86AddEnabledDevice(pInfo);
            pHyp->public.on = TRUE;
            break;
    
        case DEVICE_OFF:
            DBG(1, ErrorF("xf86HypProc  pHyp=%p what=%s\n", (void *)pHyp,
                          (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
            if (pInfo->fd >= 0)
                xf86RemoveEnabledDevice(pInfo);
            pHyp->public.on = FALSE;
            break;
    
        case DEVICE_CLOSE:
            DBG(1, ErrorF("xf86HypProc  pHyp=%p what=%s\n", (void *)pHyp,
                          (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
            SYSCALL(close(pInfo->fd));
            pInfo->fd = -1;
            break;
    
        default:
            ErrorF("unsupported mode=%d\n", what);
            return !Success;
            break;
        }
        DBG(2, ErrorF("END   xf86HypProc Success what=%d dev=%p priv=%p\n",
                      what, (void *)pHyp, (void *)priv));
        return Success;
    }
    /*
    ** xf86HypChangeControl
    ** When I figure out what it does, it will do it.
    */
    static int
    xf86HypChangeControl(InputInfoPtr pInfo, xDeviceCtl *control)
    {
        xDeviceResolutionCtl    *res;
    
        res = (xDeviceResolutionCtl *)control;
    
        if ((control->control != DEVICE_RESOLUTION) ||
            (res->num_valuators < 1))
            return (BadMatch);
    
        return(Success);
    }
    
    /*
    ** xf86HypSwitchMode
    ** Switches the mode.  For now just absolute or relative, hopefully
    ** more on the way.
    */
    static int
    xf86HypSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
    {
        InputInfoPtr    pInfo = (InputInfoPtr)dev->public.devicePrivate;
        HyperPenDevicePtr    priv = (HyperPenDevicePtr)(pInfo->private);
        char        newmode;
    
        DBG(3, ErrorF("xf86HypSwitchMode dev=%p mode=%d\n", (void *)dev, mode));
    
        switch(mode) {
        case Absolute:
            priv->flags |= ABSOLUTE_FLAG;
    
            break;
    
        case Relative:
            priv->flags &= ~ABSOLUTE_FLAG;
    
            break;
    
        default:
            DBG(1, ErrorF("xf86HypSwitchMode dev=%p invalid mode=%d\n",
                          (void *)dev, mode));
            return BadMatch;
        }
        SYSCALL(write(pInfo->fd, &newmode, 1));
        return Success;
    }
    
    /*
     * xf86HypUninit --
     *
     * called when the driver is unloaded.
     */
    static void
    xf86HypUninit(InputDriverPtr    drv,
                  InputInfoPtr    pInfo,
                  int flags)
    {
        HyperPenDevicePtr    priv = (HyperPenDevicePtr) pInfo->private;
    
        DBG(1, ErrorF("xf86HypUninit\n"));
    
        free (priv);
        pInfo->private = NULL;
        xf86DeleteInput(pInfo, 0);
    }
    
    
    /*
     * xf86HypInit --
     *
     * called when the module subsection is found in XF86Config
     */
    static int
    xf86HypInit(InputDriverPtr      drv,
                InputInfoPtr        pInfo,
                int                 flags)
    {
        HyperPenDevicePtr priv = malloc(sizeof(HyperPenDeviceRec));
        char *s;
        int rc = Success;
    
        if (!priv) {
            rc = BadAlloc;
            goto SetupProc_fail;
        }
    
        /* Serial Device is mandatory */
        priv->hypDevice = xf86FindOptionValue(pInfo->options, "Device");
    
        if (!priv->hypDevice) {
            xf86Msg (X_ERROR, "%s: No Device specified.\n", pInfo->name);
            rc = BadMatch;
            goto SetupProc_fail;
        }
    
        pInfo->private = priv;
        pInfo->device_control = xf86HypProc;
        pInfo->read_input = xf86HypReadInput;
        pInfo->control_proc = xf86HypChangeControl;
        pInfo->switch_mode = xf86HypSwitchMode;
        pInfo->type_name = "STYLUS";
        pInfo->fd = -1;
        priv->AutoPT = 1;
    
    #if defined(sun) && !defined(i386)
        if (dev_name) {
            priv->hypDevice = (char *)alloc(strlen(dev_name) + 1);
            strcpy(priv->hypDevice, dev_name);
            ErrorF("xf86HypOpen port changed to '%s'\n", priv->hypDevice);
        } else {
            priv->hypDevice = "";
        }
    #else
        priv->hypDevice = "";        /* device file name */
    #endif
    
        priv->hypOldX = -1;          /* previous X position */
        priv->hypOldY = -1;          /* previous Y position */
        priv->hypOldProximity = 0;   /* previous proximity */
        priv->hypOldButtons = 0;     /* previous buttons state */
        priv->hypMaxX = -1;          /* max X value */
        priv->hypMaxY = -1;          /* max Y value */
        priv->hypXSize = -1;         /* active area X */
        priv->hypXOffset = 1;        /* active area X offset */
        priv->hypYSize = -1;         /* active area Y */
        priv->hypYOffset = 0;        /* active area Y offset */
        priv->flags = ABSOLUTE_FLAG; /* various flags -- default abs format */
        priv->hypIndex = 0;          /* number of bytes read */
        priv->hypRes = 0;            /* resolution */
        stylus=0;
    
        /* Optional configuration */
        xf86Msg(X_CONFIG, "%s serial device is %s\n", pInfo->name,
                priv->hypDevice);
    
        debug_level = xf86SetIntOption(pInfo->options, "DebugLevel", 0);
        if (debug_level > 0) {
            xf86Msg(X_CONFIG, "%s: debug level set to %d\n", pInfo->name, debug_level);
        }
    
        s = xf86FindOptionValue(pInfo->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", pInfo->name);
        }
        xf86Msg(X_CONFIG, "%s is in %s mode\n", pInfo->name,
                (priv->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");
    
        s = xf86FindOptionValue(pInfo->options, "Cursor");
    
        if (s && (xf86NameCmp(s, "stylus") == 0)) {
            priv->flags = priv->flags | STYLUS_FLAG;
        } else if (s && (xf86NameCmp(s, "puck") == 0)) {
            priv->flags = priv->flags & ~STYLUS_FLAG;
        } else if (s) {
            xf86Msg(X_ERROR, "%s: invalid Cursor (should be stylus or puck). "
                    "Using default.\n", pInfo->name);
        }
        xf86Msg(X_CONFIG, "%s is in cursor-mode %s\n", pInfo->name,
                (priv->flags & STYLUS_FLAG) ? "cursor" : "puck");
    
        priv->hypXSize = xf86SetIntOption(pInfo->options, "XSize", 0);
        if (priv->hypXSize != 0) {
            xf86Msg(X_CONFIG, "%s: XSize = %d\n",
                    pInfo->name, priv->hypXSize);
        }
    
        priv->hypYSize = xf86SetIntOption(pInfo->options, "YSize", 0);
        if (priv->hypYSize != 0) {
            xf86Msg(X_CONFIG, "%s: YSize = %d\n",
                    pInfo->name, priv->hypYSize);
        }
    
        priv->PT = xf86SetIntOption(pInfo->options, "PMin", 0);
        if (priv->PT > 2) {
            xf86Msg(X_CONFIG, "%s: PMin = %d\n",
                    pInfo->name, priv->PT);
            priv->AutoPT = 0;
        } else
            xf86Msg(X_ERROR, "%s: invalid PMin value (should be > 2)."
                    "Using default.\n", pInfo->name);
    
        priv->PMax = xf86SetIntOption(pInfo->options, "PMax", 0);
        if (priv->PMax > 3) {
            xf86Msg(X_CONFIG, "%s: PMax = %d\n",
                    pInfo->name, priv->PMax);
        } else
            xf86Msg(X_ERROR, "%s: invalid PMax value (should be > 3)."
                    "Using default.\n", pInfo->name);
    
        priv->hypXOffset = xf86SetIntOption(pInfo->options, "XOffset", 0);
        if (priv->hypXOffset != 0) {
            xf86Msg(X_CONFIG, "%s: XOffset = %d\n",
                    pInfo->name, priv->hypXOffset);
        }
    
        priv->hypYOffset = xf86SetIntOption(pInfo->options, "YOffset", 0);
        if (priv->hypYOffset != 0) {
            xf86Msg(X_CONFIG, "%s: YOffset = %d\n",
                    pInfo->name, priv->hypYOffset);
        }
    
        if (xf86SetBoolOption(pInfo->options, "InvX", FALSE)) {
            priv->flags |= INVX_FLAG;
            xf86Msg(X_CONFIG, "%s: InvX\n", pInfo->name);
        }
    
        if (xf86SetBoolOption(pInfo->options, "InvY", FALSE)) {
            priv->flags |= INVY_FLAG;
            xf86Msg(X_CONFIG, "%s: InvY\n", pInfo->name);
        }
    
        {
            int val;
            val = xf86SetIntOption(pInfo->options, "BaudRate", 0);
            switch (val) {
            case 19200:
                priv->flags |= BAUD_19200_FLAG;
                break;
            case 9600:
                priv->flags &= ~BAUD_19200_FLAG;
                break;
            default:
                xf86Msg(X_CONFIG, "%s: Illegal speed value (must be 9600 or 19200)\n", pInfo->name);
                break;
            }
        }
    
        return rc;
    
     SetupProc_fail:
        if (priv)
            free(priv);
        if (pInfo)
            free(pInfo);
        return rc;
    }
    
    _X_EXPORT InputDriverRec HYPERPEN = {
        1,                /* driver version */
        "hyperpen",            /* driver name */
        NULL,            /* identify */
        xf86HypInit,        /* pre-init */
        xf86HypUninit,        /* un-init */
        NULL,            /* module */
        default_options
    };
    
    
    /*
    ***************************************************************************
    *
    * Dynamic loading functions
    *
    ***************************************************************************
    */
    /*
     * xf86HypUnplug --
     *
     * called when the module subsection is found in XF86Config
     */
    static void
    xf86HypUnplug(pointer    p)
    {
        DBG(1, ErrorF("xf86HypUnplug\n"));
    }
    
    /*
     * xf86HypPlug --
     *
     * called when the module subsection is found in XF86Config
     */
    static pointer
    xf86HypPlug(pointer    module,
                pointer    options,
                int        *errmaj,
                int        *errmin)
    {
        DBG(1, ErrorF("xf86HypPlug\n"));
    
        xf86AddInputDriver(&HYPERPEN, module, 0);
    
        return module;
    }
    
    static XF86ModuleVersionInfo xf86HypVersionRec =
        {
            "hyperpen",
            MODULEVENDORSTRING,
            MODINFOSTRING1,
            MODINFOSTRING2,
            XORG_VERSION_CURRENT,
            PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
            ABI_CLASS_XINPUT,
            ABI_XINPUT_VERSION,
            MOD_CLASS_XINPUT,
            {0, 0, 0, 0}        /* signature, to be patched into the file by */
            /* a tool */
        };
    
    _X_EXPORT XF86ModuleData hyperpenModuleData = {
        &xf86HypVersionRec,
        xf86HypPlug,
        xf86HypUnplug
    };
    
    /* end of xf86HyperPen.c */