LWN.net Logo

[PATCH 2.6.20 1/1] usb/input updated aiptek tablet driver

From:  "Rene van Paassen" <rene.vanpaassen@gmail.com>
To:  linux-usb-devel@lists.sourceforge.net
Subject:  [PATCH 2.6.20 1/1] usb/input updated aiptek tablet driver
Date:  Thu, 8 Mar 2007 00:02:48 +0100
Cc:  aiptektablet-users@lists.sourceforge.net
Archive-link:  Article, Thread

Hello all,

This is a patch for the aiptek tablet input driver. It is the result
of development work done
on this driver at aiptektablet.sourceforce.net

A summary of the important changes:

- corrected proximity (p) and data valid (dv) flags.
- corrected mouse button defines
- corrected problems with relative mode
- function keys of the tablet now are functional
- corrected specification of tablet capabilities; using set_bit()
  old code was wrong for bits > 32
- code now includes a proximity timeout, for tablets that do not
  send p=0 (out of proximity) and dv=1 (valid report). Only enable
  for buggy tablets or people who really quickly jerk the stylus from
  the tablet.

Signed-off-by: René van Paassen <Rene.vanPaassen@gmail.com>

--- linux-2.6.20/drivers/usb/input/aiptek.c.orig        2007-02-04
19:44:54.000000000 +0100
+++ linux-2.6.20/drivers/usb/input/aiptek.c     2007-03-05
22:52:51.000000000 +0100
@@ -4,6 +4,7 @@
  *
  *  Copyright (c) 2001      Chris Atenasio   <chris@crud.net>
  *  Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
+ *  Extra bits    2004      René van Paassen <M.M.vanPaassen@tudelft.nl>
  *
  *  based on wacom.c by
  *     Vojtech Pavlik      <vojtech@suse.cz>
@@ -45,6 +46,38 @@
  *             Feb 20, 2004, Bryan W. Headley.
  *      v1.5 - Added previousJitterable, so we don't do jitter delay when the
  *             user is holding a button down for periods of time.
+ *      v1.6 - Corrected mouse button detection, defines were off (RvP)
+ *             Corrected macro button detection. On my tablet (Medion, is
+ *             reported as Aiptek 12000U, data[3] = 1 is just left of F1,
+ *             2 = F1, 3 = between F1 and F2, etc.
+ *             added a lastMacro variable, so macro key is reset if you drag
+ *             mouse/stylus from macro into the drawing area.
+ *      v1.7 - switched to strncmp for string comparison, to accept trailing
+ *             returns (RvP)
+ *      v1.8 - Putting a new timer (add_timer, del_timer) in the system.
+ *             The problem is now that the tablet never reports
+ *             "out of proximity" events (at least mine doesn't). So my
+ *             answer is sending those events after a time of inactivity.
+ *             is what the timer is for.
+ *             got bitten by the fact that the ABS_MISC event moved up
+ *             by a long in the bit array. To celebrate the detection of
+ *             this bug, started to neatly use set_bit(), instead of crossing
+ *             fingers and mucking with the bitmaps directly (RvP)
+ *      v1.9 - Moved the inactivity timer removal to correct place, removed
+ *             timeout debugging message, added removal proximity timeout file
+ *             (bugfixes). Added fix by the masc (ms - at - sbox.tugraz.at)
+ *             This fix also fixes the problem of lacking out-of-proximity
+ *             reports (at least with my tablet). So I made the proximity
+ *             timeout conditional. Apparently, for my tablet is it not needed
+ *             (with the fixed detection of proximity and data valid). Kept
+ *             the code in, for now. (nov 19, 2004 RvP)
+ *      v1.9.1 Merged in extant patches, did some constant elimination. BwH.
+ *      v1.9.2 Merged in patches in signatures to show_# and store_# routines.
+ *      v2.0 - Switched to using input_allocate_device and usb_kill_urb.
+ *             Ran the code through "indent -kr -i8", to get Linux kernel
+ *             indenting style + removed superfluous brakets in case
+ *             statements. Apr 19, 2006, RvP
+ *      v2.1   Preparing for merge with kernel RvP
  *
  * NOTE:
  *      This kernel driver is augmented by the "Aiptek" XFree86 input
@@ -77,14 +110,15 @@
 #include <linux/init.h>
 #include <linux/usb/input.h>
 #include <linux/sched.h>
+#include <linux/timer.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>

 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.5 (May-15-2004)"
-#define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio"
+#define DRIVER_VERSION "v2.1 (Feb-14-2007)"
+#define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio/Cédric
Brun/René van Paassen"
 #define DRIVER_DESC    "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"

 /*
@@ -265,9 +299,9 @@

        /* Mouse button programming
         */
-#define AIPTEK_MOUSE_LEFT_BUTTON               0x01
-#define AIPTEK_MOUSE_RIGHT_BUTTON              0x02
-#define AIPTEK_MOUSE_MIDDLE_BUTTON             0x04
+#define AIPTEK_MOUSE_LEFT_BUTTON               0x04
+#define AIPTEK_MOUSE_RIGHT_BUTTON              0x08
+#define AIPTEK_MOUSE_MIDDLE_BUTTON             0x10

        /* Stylus button programming
         */
@@ -287,6 +321,12 @@
 #define AIPTEK_REPORT_TOOL_STYLUS              0x20
 #define AIPTEK_REPORT_TOOL_MOUSE               0x40

+       /* Debug levels - higher means more output.
+        */
+#define AIPTEK_DEBUG_CONFIG                    1
+#define AIPTEK_DEBUG_TRACE                     2
+#define AIPTEK_DEBUG_IRQTRACE                  5
+
 static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
 static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;

@@ -312,9 +352,11 @@
        int mouseButtonRight;   /* mouse right btn delivers...   */
        int programmableDelay;  /* delay for tablet programming  */
        int jitterDelay;        /* delay for hand jittering      */
+       int proximityTimeout;   /* reset proximity after this    */
 };

 struct aiptek {
+       int debug;                              /* Debug level; 0 =no
debug      */
        struct input_dev *inputdev;             /* input device struct
          */
        struct usb_device *usbdev;              /* usb device struct
          */
        struct urb *urb;                        /* urb for incoming
reports      */
@@ -328,7 +370,9 @@
        int inDelay;                            /* jitter: in jitter
delay?      */
        unsigned long endDelay;                 /* jitter: time when
delay ends  */
        int previousJitterable;                 /* jitterable prev value     */
+       int lastMacro;                          /* last macro key,
might need resetting */
        unsigned char *data;                    /* incoming packet
data          */
+       struct timer_list activityCheck;        /* timer  for
monitoring prox    */
 };

 /*
@@ -346,6 +390,36 @@
 };

 /***********************************************************************
+ * Constants used in the sysfs files. This affords us a small size
+ * optimization, plus maintains a single point of failure (misspellings.)
+ */
+static char *pStylus = "stylus";
+static char *pMouse = "mouse";
+static char *pEither = "either";
+static char *pUnknown = "unknown";
+static char *pAbsolute = "absolute";
+static char *pRelative = "relative";
+static char *pEraser = "eraser";
+static char *pPen = "pen";
+static char *pPencil = "pencil";
+static char *pBrush = "brush";
+static char *pAirbrush = "airbrush";
+static char *pLens = "lens";
+static char *pDisable = "disable";
+static char *pUpper = "upper";
+static char *pLower = "lower";
+static char *pLeft = "left";
+static char *pRight = "right";
+static char *pMiddle = "middle";
+
+/***********************************************************************
+ * Very common printf format strings for the sysfs files.
+ */
+static char *pString = "%s\n";
+static char *pInt = "%d\n";
+static char *pHex = "0x%04x\n";
+
+/***********************************************************************
  * Relative reports deliver values in 2's complement format to
  * deal with negative offsets.
  */
@@ -432,10 +506,21 @@
        aiptek->inDelay = 0;
        aiptek->eventCount++;

+       /* Now remove the inactivity timer, if it is still lurking around. */
+       if (aiptek->curSetting.proximityTimeout) {
+               del_timer(&aiptek->activityCheck);
+       }
+
        /* Report 1 delivers relative coordinates with either a stylus
         * or the mouse. You do not know, however, which input
         * tool generated the event.
+        * Update 070228/RvP My tablet, a Medion / re-branded 12000U,
+        * reports x=-255 for the stylus, and y=-255 for the mouse in
+        * this mode, apparently when x resp y should be zero.
         */
+       if (aiptek->debug == AIPTEK_DEBUG_IRQTRACE) {
+               info("irq(%d): report id = %d\n", __LINE__, data[0]);
+       }
        if (data[0] == 1) {
                if (aiptek->curSetting.coordinateMode ==
                    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
@@ -461,9 +546,23 @@
                        input_report_key(inputdev, BTN_LEFT, left);
                        input_report_key(inputdev, BTN_MIDDLE, middle);
                        input_report_key(inputdev, BTN_RIGHT, right);
+
+                       /* Stylus or Mouse usage is reported in a peculiar
+                        * way. Report tool usage, and correct displacement */
+                       if (x == -255) {
+                               input_report_abs
+                                       (inputdev, ABS_MISC,
+                                        1 | AIPTEK_REPORT_TOOL_STYLUS);
+                               x = 0;
+                       }
+                       if (y == -255) {
+                               input_report_abs
+                                       (inputdev, ABS_MISC,
+                                        1 | AIPTEK_REPORT_TOOL_MOUSE);
+                               y = 0;
+                       }
                        input_report_rel(inputdev, REL_X, x);
                        input_report_rel(inputdev, REL_Y, y);
-                       input_report_rel(inputdev, REL_MISC, 1 |
AIPTEK_REPORT_TOOL_UNKNOWN);

                        /* Wheel support is in the form of a single-event
                         * firing.
@@ -473,6 +572,9 @@
                                                 aiptek->curSetting.wheel);
                                aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
                        }
+                       input_report_key(inputdev,
+                                        macroKeyEvents[aiptek->lastMacro],
+                                        0);
                        input_sync(inputdev);
                }
        }
@@ -490,8 +592,8 @@
                        y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
                        z = le16_to_cpu(get_unaligned((__le16 *) (data + 6)));

-                       p = (data[5] & 0x01) != 0 ? 1 : 0;
-                       dv = (data[5] & 0x02) != 0 ? 1 : 0;
+                       dv = (data[5] & 0x01) != 0 ? 1 : 0;
+                       p = (data[5] & 0x02) != 0 ? 1 : 0;
                        tip = (data[5] & 0x04) != 0 ? 1 : 0;

                        /* Use jitterable to re-arrange button masks
@@ -501,6 +603,10 @@
                        bs = (data[5] &
aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
                        pck = (data[5] &
aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;

+                       /* Proximity bit not set when bs or pck set
+                        */
+                       p |= bs | pck;
+
                        /* dv indicates 'data valid' (e.g., the tablet
is in sync
                         * and has delivered a "correct" report) We will ignore
                         * all 'bad' reports...
@@ -516,18 +622,22 @@

TOOL_BUTTON(aiptek->curSetting.toolMode),
                                                         1);
                                        aiptek->curSetting.toolMode |=
TOOL_BUTTON_FIRED_BIT;
+                               } else {
+                                       input_report_key(inputdev,
+
TOOL_BUTTON(aiptek->curSetting.toolMode),
+                                                        0);
                                }

                                if (p != 0) {
-                                       input_report_abs(inputdev, ABS_X, x);
-                                       input_report_abs(inputdev, ABS_Y, y);
-                                       input_report_abs(inputdev,
ABS_PRESSURE, z);
-
                                        input_report_key(inputdev,
BTN_TOUCH, tip);
                                        input_report_key(inputdev,
BTN_STYLUS, bs);
                                        input_report_key(inputdev,
BTN_STYLUS2, pck);

-                                       if (aiptek->curSetting.xTilt !=
+                                       input_report_abs(inputdev, ABS_X, x);
+                                       input_report_abs(inputdev, ABS_Y, y);
+                                       input_report_abs(inputdev,
ABS_PRESSURE, z);
+
+                                       if (aiptek->curSetting.xTilt !=
                                            AIPTEK_TILT_DISABLE) {
                                                input_report_abs(inputdev,
                                                                 ABS_TILT_X,
@@ -551,6 +661,10 @@
                                        }
                                }
                                input_report_abs(inputdev, ABS_MISC, p
| AIPTEK_REPORT_TOOL_STYLUS);
+                               input_report_key(inputdev,
+                                                macroKeyEvents[aiptek->
+                                                               lastMacro],
+                                                0);
                                input_sync(inputdev);
                        }
                }
@@ -569,8 +683,8 @@

                        jitterable = data[5] & 0x1c;

-                       p = (data[5] & 0x01) != 0 ? 1 : 0;
-                       dv = (data[5] & 0x02) != 0 ? 1 : 0;
+                       dv = (data[5] & 0x01) != 0 ? 1 : 0;
+                       p = (data[5] & 0x02) != 0 ? 1 : 0;
                        left = (data[5] &
aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
                        right = (data[5] &
aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
                        middle = (data[5] &
aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
@@ -586,6 +700,9 @@

TOOL_BUTTON(aiptek->curSetting.toolMode),
                                                         1);
                                        aiptek->curSetting.toolMode |=
TOOL_BUTTON_FIRED_BIT;
+                               } else {
+                                       input_report_key(inputdev,
+
TOOL_BUTTON(aiptek->curSetting.toolMode), 0);
                                }

                                if (p != 0) {
@@ -606,7 +723,9 @@

aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
                                        }
                                }
-                               input_report_rel(inputdev, REL_MISC, p
| AIPTEK_REPORT_TOOL_MOUSE);
+                               input_report_abs(inputdev, ABS_MISC, p
| AIPTEK_REPORT_TOOL_MOUSE);
+                               input_report_key(inputdev,
+
macroKeyEvents[aiptek->lastMacro], 0);
                                input_sync(inputdev);
                        }
                }
@@ -616,13 +735,12 @@
        else if (data[0] == 4) {
                jitterable = data[1] & 0x18;

-               p = (data[1] & 0x01) != 0 ? 1 : 0;
-               dv = (data[1] & 0x02) != 0 ? 1 : 0;
+               dv = (data[1] & 0x01) != 0 ? 1 : 0;
+               p = (data[1] & 0x02) != 0 ? 1 : 0;
                tip = (data[1] & 0x04) != 0 ? 1 : 0;
                bs = (data[1] & aiptek->curSetting.stylusButtonLower)
!= 0 ? 1 : 0;
                pck = (data[1] & aiptek->curSetting.stylusButtonUpper)
!= 0 ? 1 : 0;
-
-               macro = data[3];
+               macro = data[3] >> 1;
                z = le16_to_cpu(get_unaligned((__le16 *) (data + 4)));

                if (dv != 0) {
@@ -635,29 +753,25 @@

TOOL_BUTTON(aiptek->curSetting.toolMode),
                                                 1);
                                aiptek->curSetting.toolMode |=
TOOL_BUTTON_FIRED_BIT;
+                       } else {
+                               input_report_key(inputdev,
+
TOOL_BUTTON(aiptek->curSetting.toolMode), 0);
                        }

-                       if (p != 0) {
-                               input_report_key(inputdev, BTN_TOUCH, tip);
-                               input_report_key(inputdev, BTN_STYLUS, bs);
-                               input_report_key(inputdev, BTN_STYLUS2, pck);
-                               input_report_abs(inputdev, ABS_PRESSURE, z);
-                       }
-
-                       /* For safety, we're sending key 'break' codes for the
-                        * neighboring macro keys.
+                       /* This now expressly resets the key on the
+                        * areas (reported as odd by the tablet)
+                        * between the macro key areas. Only report
+                        * key when tip is pressed.
                         */
-                       if (macro > 0) {
-                               input_report_key(inputdev,
-                                                macroKeyEvents[macro - 1], 0);
-                       }
-                       if (macro < 25) {
-                               input_report_key(inputdev,
-                                                macroKeyEvents[macro + 1], 0);
-                       }
-                       input_report_key(inputdev, macroKeyEvents[macro], p);
+                       input_report_key(inputdev, macroKeyEvents[macro],
+                                        tip & ~data[3]);
                        input_report_abs(inputdev, ABS_MISC,
                                         p | AIPTEK_REPORT_TOOL_STYLUS);
+
+                       /* remember this key. If the user now drags stylus
+                        * into the drawing area it needs to be reset.
+                        */
+                       aiptek->lastMacro = macro;
                        input_sync(inputdev);
                }
        }
@@ -666,12 +780,12 @@
        else if (data[0] == 5) {
                jitterable = data[1] & 0x1c;

-               p = (data[1] & 0x01) != 0 ? 1 : 0;
-               dv = (data[1] & 0x02) != 0 ? 1 : 0;
-               left = (data[1]& aiptek->curSetting.mouseButtonLeft)
!= 0 ? 1 : 0;
+               dv = (data[1] & 0x01) != 0 ? 1 : 0;
+               p = (data[1] & 0x02) != 0 ? 1 : 0;
+               left = (data[1] & aiptek->curSetting.mouseButtonLeft)
!= 0 ? 1 : 0;
                right = (data[1] &
aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
                middle = (data[1] &
aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
-               macro = data[3];
+               macro = data[3] >> 1;

                if (dv != 0) {
                        /* If we've not already sent a tool_button_?? code, do
@@ -683,29 +797,18 @@

TOOL_BUTTON(aiptek->curSetting.toolMode),
                                                 1);
                                aiptek->curSetting.toolMode |=
TOOL_BUTTON_FIRED_BIT;
-                       }
-
-                       if (p != 0) {
-                               input_report_key(inputdev, BTN_LEFT, left);
-                               input_report_key(inputdev, BTN_MIDDLE, middle);
-                               input_report_key(inputdev, BTN_RIGHT, right);
-                       }
-
-                       /* For safety, we're sending key 'break' codes for the
-                        * neighboring macro keys.
-                        */
-                       if (macro > 0) {
-                               input_report_key(inputdev,
-                                                macroKeyEvents[macro - 1], 0);
-                       }
-                       if (macro < 25) {
+                       } else {
                                input_report_key(inputdev,
-                                                macroKeyEvents[macro + 1], 0);
+                                                TOOL_BUTTON(aiptek->
+                                                            curSetting.
+                                                            toolMode), 0);
                        }

-                       input_report_key(inputdev, macroKeyEvents[macro], 1);
-                       input_report_rel(inputdev, ABS_MISC,
+                       input_report_key(inputdev, macroKeyEvents[macro],
+                                        left & ~data[3]);
+                       input_report_abs(inputdev, ABS_MISC,
                                         p | AIPTEK_REPORT_TOOL_MOUSE);
+                       aiptek->lastMacro = macro;
                        input_sync(inputdev);
                }
        }
@@ -735,6 +838,10 @@
                                         TOOL_BUTTON(aiptek->curSetting.
                                                     toolMode), 1);
                        aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+               } else {
+                       input_report_key(inputdev,
+                                        TOOL_BUTTON(aiptek->curSetting.
+                                                    toolMode), 0);
                }

                input_report_key(inputdev, macroKeyEvents[macro], 1);
@@ -745,10 +852,10 @@
                dbg("Unknown report %d", data[0]);
        }

-       /* Jitter may occur when the user presses a button on the stlyus
+       /* Jitter may occur when the user presses a button on the stylus
         * or the mouse. What we do to prevent that is wait 'x' milliseconds
         * following a 'jitterable' event, which should give the hand some time
-        * stabilize itself.
+        * to stabilize itself.
         *
         * We just introduced aiptek->previousJitterable to carry forth the
         * notion that jitter occurs when the button state changes
from on to off:
@@ -765,7 +872,15 @@
        }
        aiptek->previousJitterable = jitterable;

-exit:
+       /* set the inactivity timer. Take due account for the jitter
delay too. */
+       if (aiptek->curSetting.proximityTimeout) {
+               aiptek->activityCheck.expires =
+                   (aiptek->inDelay ? aiptek->endDelay : jiffies) +
+                   aiptek->curSetting.proximityTimeout;
+               add_timer(&aiptek->activityCheck);
+       }
+
+      exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval != 0) {
                err("%s - usb_submit_urb failed with result %d",
@@ -872,6 +987,9 @@
                    command, data);
        }
        kfree(buf);
+
+       msleep(aiptek->curSetting.programmableDelay);
+
        return ret < 0 ? ret : 0;
 }

@@ -972,6 +1090,7 @@
                        return ret;
                }
        }
+       msleep(160);

        /* Enable the macro keys */
        if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
@@ -1002,6 +1121,38 @@
  */

 /***********************************************************************
+ * Add dynamic debugging.
+ */
+static ssize_t show_tabletDebug(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct aiptek *aiptek = dev_get_drvdata(dev);
+
+       if (aiptek == NULL) {
+               return 0;
+       }
+
+       return snprintf(buf, PAGE_SIZE, pInt, aiptek->debug);
+}
+
+static ssize_t
+store_tabletDebug(struct device *dev,
+                 struct device_attribute *attr,
+                 const char *buf, size_t count)
+{
+       struct aiptek *aiptek = dev_get_drvdata(dev);
+       if (aiptek == NULL) {
+               return 0;
+       }
+
+       aiptek->debug = (int) simple_strtol(buf, 0, 10);
+       return count;
+}
+
+static DEVICE_ATTR(debug, S_IRUGO | S_IWUGO, show_tabletDebug,
+                  store_tabletDebug);
+
+/***********************************************************************
  * support the 'size' file -- display support
  */
 static ssize_t show_tabletSize(struct device *dev, struct
device_attribute *attr, char *buf)
@@ -1034,8 +1185,7 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "0x%04x\n",
-                       aiptek->inputdev->id.product);
+       return snprintf(buf, PAGE_SIZE, pHex, aiptek->inputdev->id.product);
 }

 static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL);
@@ -1050,7 +1200,7 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "0x%04x\n",
aiptek->inputdev->id.vendor);
+       return snprintf(buf, PAGE_SIZE, pHex, aiptek->inputdev->id.vendor);
 }

 static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL);
@@ -1066,7 +1216,7 @@
        if (aiptek == NULL)
                return 0;

-       retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer);
+       retval = snprintf(buf, PAGE_SIZE, pString,
aiptek->usbdev->manufacturer);
        return retval;
 }

@@ -1083,7 +1233,7 @@
        if (aiptek == NULL)
                return 0;

-       retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product);
+       retval = snprintf(buf, PAGE_SIZE, pString, aiptek->usbdev->product);
        return retval;
 }

@@ -1103,22 +1253,22 @@

        switch (aiptek->curSetting.pointerMode) {
        case AIPTEK_POINTER_ONLY_STYLUS_MODE:
-               s = "stylus";
+               s = pStylus;
                break;

        case AIPTEK_POINTER_ONLY_MOUSE_MODE:
-               s = "mouse";
+               s = pMouse;
                break;

        case AIPTEK_POINTER_EITHER_MODE:
-               s = "either";
+               s = pEither;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1128,12 +1278,12 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "stylus") == 0) {
+       if (strncmp(buf, pStylus, 6) == 0) {
                aiptek->newSetting.pointerMode =
                    AIPTEK_POINTER_ONLY_STYLUS_MODE;
-       } else if (strcmp(buf, "mouse") == 0) {
+       } else if (strncmp(buf, pMouse, 5) == 0) {
                aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE;
-       } else if (strcmp(buf, "either") == 0) {
+       } else if (strncmp(buf, pEither, 6) == 0) {
                aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
        }
        return count;
@@ -1157,18 +1307,18 @@

        switch (aiptek->curSetting.coordinateMode) {
        case AIPTEK_COORDINATE_ABSOLUTE_MODE:
-               s = "absolute";
+               s = pAbsolute;
                break;

        case AIPTEK_COORDINATE_RELATIVE_MODE:
-               s = "relative";
+               s = pRelative;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1178,11 +1328,11 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "absolute") == 0) {
-               aiptek->newSetting.pointerMode =
+       if (strncmp(buf, pAbsolute, 8) == 0) {
+               aiptek->newSetting.coordinateMode =
                    AIPTEK_COORDINATE_ABSOLUTE_MODE;
-       } else if (strcmp(buf, "relative") == 0) {
-               aiptek->newSetting.pointerMode =
+       } else if (strncmp(buf, pRelative, 8) == 0) {
+               aiptek->newSetting.coordinateMode =
                    AIPTEK_COORDINATE_RELATIVE_MODE;
        }
        return count;
@@ -1206,38 +1356,38 @@

        switch (TOOL_BUTTON(aiptek->curSetting.toolMode)) {
        case AIPTEK_TOOL_BUTTON_MOUSE_MODE:
-               s = "mouse";
+               s = pMouse;
                break;

        case AIPTEK_TOOL_BUTTON_ERASER_MODE:
-               s = "eraser";
+               s = pEraser;
                break;

        case AIPTEK_TOOL_BUTTON_PENCIL_MODE:
-               s = "pencil";
+               s = pPencil;
                break;

        case AIPTEK_TOOL_BUTTON_PEN_MODE:
-               s = "pen";
+               s = pPen;
                break;

        case AIPTEK_TOOL_BUTTON_BRUSH_MODE:
-               s = "brush";
+               s = pBrush;
                break;

        case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE:
-               s = "airbrush";
+               s = pAirbrush;
                break;

        case AIPTEK_TOOL_BUTTON_LENS_MODE:
-               s = "lens";
+               s = pLens;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1247,19 +1397,19 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "mouse") == 0) {
+       if (strncmp(buf, pMouse, 5) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE;
-       } else if (strcmp(buf, "eraser") == 0) {
+       } else if (strncmp(buf, pEraser, 6) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE;
-       } else if (strcmp(buf, "pencil") == 0) {
+       } else if (strncmp(buf, pPencil, 6) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE;
-       } else if (strcmp(buf, "pen") == 0) {
+       } else if (strncmp(buf, pPen, 3) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
-       } else if (strcmp(buf, "brush") == 0) {
+       } else if (strncmp(buf, pBrush, 5) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE;
-       } else if (strcmp(buf, "airbrush") == 0) {
+       } else if (strncmp(buf, pAirbrush, 8) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE;
-       } else if (strcmp(buf, "lens") == 0) {
+       } else if (strncmp(buf, pLens, 4) == 0) {
                aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE;
        }

@@ -1282,9 +1432,9 @@
                return 0;

        if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
-               return snprintf(buf, PAGE_SIZE, "disable\n");
+               return snprintf(buf, PAGE_SIZE, pString, pDisable);
        } else {
-               return snprintf(buf, PAGE_SIZE, "%d\n",
+               return snprintf(buf, PAGE_SIZE, pInt,
                                aiptek->curSetting.xTilt);
        }
 }
@@ -1298,10 +1448,10 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "disable") == 0) {
+       if (strncmp(buf, pDisable, 7) == 0) {
                aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE;
        } else {
-               x = (int)simple_strtol(buf, NULL, 10);
+               x = (int) simple_strtol(buf, 0, 10);
                if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) {
                        aiptek->newSetting.xTilt = x;
                }
@@ -1324,9 +1474,9 @@
                return 0;

        if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
-               return snprintf(buf, PAGE_SIZE, "disable\n");
+               return snprintf(buf, PAGE_SIZE, pString, pDisable);
        } else {
-               return snprintf(buf, PAGE_SIZE, "%d\n",
+               return snprintf(buf, PAGE_SIZE, pInt,
                                aiptek->curSetting.yTilt);
        }
 }
@@ -1340,10 +1490,10 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "disable") == 0) {
+       if (strncmp(buf, pDisable, 7) == 0) {
                aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE;
        } else {
-               y = (int)simple_strtol(buf, NULL, 10);
+               y = (int) simple_strtol(buf, 0, 10);
                if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) {
                        aiptek->newSetting.yTilt = y;
                }
@@ -1365,7 +1515,8 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
+       return snprintf(buf, PAGE_SIZE, pInt,
+                       aiptek->curSetting.jitterDelay);
 }

 static ssize_t
@@ -1385,6 +1536,43 @@
                   show_tabletJitterDelay, store_tabletJitterDelay);

 /***********************************************************************
+ * support routines for the proximity_timeout file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletProximityTimeout(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct aiptek *aiptek = dev_get_drvdata(dev);
+
+       if (aiptek == NULL)
+               return 0;
+
+       return snprintf(buf, PAGE_SIZE, pInt,
+                       (aiptek->curSetting.proximityTimeout * 1000) / HZ);
+}
+
+static ssize_t
+store_tabletProximityTimeout(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       struct aiptek *aiptek = dev_get_drvdata(dev);
+
+       if (aiptek == NULL)
+               return 0;
+
+       aiptek->newSetting.proximityTimeout =
+           ((int) simple_strtol(buf, 0, 10) * HZ) / 1000;
+       return count;
+}
+
+static DEVICE_ATTR(proximity_timeout,
+                  S_IRUGO | S_IWUGO,
+                  show_tabletProximityTimeout,
+                  store_tabletProximityTimeout);
+
+/***********************************************************************
  * support routines for the 'delay' file. Note that this file
  * both displays current setting and allows reprogramming.
  */
@@ -1395,7 +1583,7 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "%d\n",
+       return snprintf(buf, PAGE_SIZE, pInt,
                        aiptek->curSetting.programmableDelay);
 }

@@ -1407,7 +1595,7 @@
        if (aiptek == NULL)
                return 0;

-       aiptek->newSetting.programmableDelay = (int)simple_strtol(buf,
NULL, 10);
+       aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, 0, 10);
        return count;
 }

@@ -1436,12 +1624,15 @@
  * support routines for the 'event_count' file. Note that this file
  * only displays current setting.
  */
-static ssize_t show_tabletEventsReceived(struct device *dev, struct
device_attribute *attr, char *buf)
+static ssize_t show_tabletEventsReceived(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
 {
        struct aiptek *aiptek = dev_get_drvdata(dev);

-       if (aiptek == NULL)
+       if (aiptek == NULL) {
                return 0;
+       }

        return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
 }
@@ -1504,18 +1695,18 @@

        switch (aiptek->curSetting.stylusButtonUpper) {
        case AIPTEK_STYLUS_UPPER_BUTTON:
-               s = "upper";
+               s = pUpper;
                break;

        case AIPTEK_STYLUS_LOWER_BUTTON:
-               s = "lower";
+               s = pLower;
                break;

        default:
-               s = "unknown";
-               break;
+               s = pUnknown;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1526,10 +1717,10 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "upper") == 0) {
+       if (strncmp(buf, pUpper, 5) == 0) {
                aiptek->newSetting.stylusButtonUpper =
                    AIPTEK_STYLUS_UPPER_BUTTON;
-       } else if (strcmp(buf, "lower") == 0) {
+       } else if (strncmp(buf, pLower, 5) == 0) {
                aiptek->newSetting.stylusButtonUpper =
                    AIPTEK_STYLUS_LOWER_BUTTON;
        }
@@ -1554,18 +1745,18 @@

        switch (aiptek->curSetting.stylusButtonLower) {
        case AIPTEK_STYLUS_UPPER_BUTTON:
-               s = "upper";
+               s = pUpper;
                break;

        case AIPTEK_STYLUS_LOWER_BUTTON:
-               s = "lower";
+               s = pLower;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1576,10 +1767,10 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "upper") == 0) {
+       if (strncmp(buf, pUpper, 5) == 0) {
                aiptek->newSetting.stylusButtonLower =
                    AIPTEK_STYLUS_UPPER_BUTTON;
-       } else if (strcmp(buf, "lower") == 0) {
+       } else if (strncmp(buf, pLower, 5) == 0) {
                aiptek->newSetting.stylusButtonLower =
                    AIPTEK_STYLUS_LOWER_BUTTON;
        }
@@ -1604,22 +1795,22 @@

        switch (aiptek->curSetting.mouseButtonLeft) {
        case AIPTEK_MOUSE_LEFT_BUTTON:
-               s = "left";
+               s = pLeft;
                break;

        case AIPTEK_MOUSE_MIDDLE_BUTTON:
-               s = "middle";
+               s = pMiddle;
                break;

        case AIPTEK_MOUSE_RIGHT_BUTTON:
-               s = "right";
+               s = pRight;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1630,11 +1821,11 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "left") == 0) {
+       if (strncmp(buf, pLeft, 4) == 0) {
                aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
-       } else if (strcmp(buf, "middle") == 0) {
+       } else if (strncmp(buf, pMiddle, 6) == 0) {
                aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON;
-       } else if (strcmp(buf, "right") == 0) {
+       } else if (strncmp(buf, pRight, 5) == 0) {
                aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON;
        }
        return count;
@@ -1658,22 +1849,22 @@

        switch (aiptek->curSetting.mouseButtonMiddle) {
        case AIPTEK_MOUSE_LEFT_BUTTON:
-               s = "left";
+               s = pLeft;
                break;

        case AIPTEK_MOUSE_MIDDLE_BUTTON:
-               s = "middle";
+               s = pMiddle;
                break;

        case AIPTEK_MOUSE_RIGHT_BUTTON:
-               s = "right";
+               s = pRight;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1684,12 +1875,13 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "left") == 0) {
-               aiptek->newSetting.mouseButtonMiddle = AIPTEK_MOUSE_LEFT_BUTTON;
-       } else if (strcmp(buf, "middle") == 0) {
+       if (strncmp(buf, pLeft, 4) == 0) {
+               aiptek->newSetting.mouseButtonMiddle =
+                   AIPTEK_MOUSE_LEFT_BUTTON;
+       } else if (strncmp(buf, pMiddle, 6) == 0) {
                aiptek->newSetting.mouseButtonMiddle =
                    AIPTEK_MOUSE_MIDDLE_BUTTON;
-       } else if (strcmp(buf, "right") == 0) {
+       } else if (strncmp(buf, pRight, 5) == 0) {
                aiptek->newSetting.mouseButtonMiddle =
                    AIPTEK_MOUSE_RIGHT_BUTTON;
        }
@@ -1701,7 +1893,7 @@
                   show_tabletMouseMiddle, store_tabletMouseMiddle);

 /***********************************************************************
- * support routines for the 'mouse_right' file. Note that this file
+ * support routines for the 'mouse_right' File. Note that this file
  * both displays current setting and allows for setting changing.
  */
 static ssize_t show_tabletMouseRight(struct device *dev, struct
device_attribute *attr, char *buf)
@@ -1714,22 +1906,22 @@

        switch (aiptek->curSetting.mouseButtonRight) {
        case AIPTEK_MOUSE_LEFT_BUTTON:
-               s = "left";
+               s = pLeft;
                break;

        case AIPTEK_MOUSE_MIDDLE_BUTTON:
-               s = "middle";
+               s = pMiddle;
                break;

        case AIPTEK_MOUSE_RIGHT_BUTTON:
-               s = "right";
+               s = pRight;
                break;

        default:
-               s = "unknown";
+               s = pUnknown;
                break;
        }
-       return snprintf(buf, PAGE_SIZE, "%s\n", s);
+       return snprintf(buf, PAGE_SIZE, pString, s);
 }

 static ssize_t
@@ -1740,13 +1932,15 @@
        if (aiptek == NULL)
                return 0;

-       if (strcmp(buf, "left") == 0) {
-               aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_LEFT_BUTTON;
-       } else if (strcmp(buf, "middle") == 0) {
+       if (strncmp(buf, pLeft, 4) == 0) {
+               aiptek->newSetting.mouseButtonRight =
+                   AIPTEK_MOUSE_LEFT_BUTTON;
+       } else if (strncmp(buf, pMiddle, 6) == 0) {
                aiptek->newSetting.mouseButtonRight =
                    AIPTEK_MOUSE_MIDDLE_BUTTON;
-       } else if (strcmp(buf, "right") == 0) {
-               aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
+       } else if (strncmp(buf, pRight, 5) == 0) {
+               aiptek->newSetting.mouseButtonRight =
+                   AIPTEK_MOUSE_RIGHT_BUTTON;
        }
        return count;
 }
@@ -1767,9 +1961,9 @@
                return 0;

        if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
-               return snprintf(buf, PAGE_SIZE, "disable\n");
+               return snprintf(buf, PAGE_SIZE, pString, pDisable);
        } else {
-               return snprintf(buf, PAGE_SIZE, "%d\n",
+               return snprintf(buf, PAGE_SIZE, pInt,
                                aiptek->curSetting.wheel);
        }
 }
@@ -1782,7 +1976,7 @@
        if (aiptek == NULL)
                return 0;

-       aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10);
+       aiptek->newSetting.wheel = (int) simple_strtol(buf, 0, 10);
        return count;
 }

@@ -1821,14 +2015,16 @@
        memcpy(&aiptek->curSetting, &aiptek->newSetting,
               sizeof(struct aiptek_settings));

-       if (aiptek_program_tablet(aiptek) < 0)
+       if (aiptek_program_tablet(aiptek) < 0) {
                return -EIO;
+       }

        return count;
 }

 static DEVICE_ATTR(execute,
-                  S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute);
+                  S_IRUGO | S_IWUGO, show_tabletExecute,
+                  store_tabletExecute);

 /***********************************************************************
  * support routines for the 'odm_code' file. Note that this file
@@ -1841,7 +2037,7 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
+       return snprintf(buf, PAGE_SIZE, pHex, aiptek->features.odmCode);
 }

 static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
@@ -1857,7 +2053,7 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
+       return snprintf(buf, PAGE_SIZE, pHex, aiptek->features.modelCode);
 }

 static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
@@ -1873,7 +2069,7 @@
        if (aiptek == NULL)
                return 0;

-       return snprintf(buf, PAGE_SIZE, "%04x\n",
+       return snprintf(buf, PAGE_SIZE, pHex,
                        aiptek->features.firmwareCode);
 }

@@ -1885,6 +2081,7 @@
  */
 static void aiptek_delete_files(struct device *dev)
 {
+       device_remove_file(dev, &dev_attr_debug);
        device_remove_file(dev, &dev_attr_size);
        device_remove_file(dev, &dev_attr_product_id);
        device_remove_file(dev, &dev_attr_vendor_id);
@@ -1896,6 +2093,7 @@
        device_remove_file(dev, &dev_attr_xtilt);
        device_remove_file(dev, &dev_attr_ytilt);
        device_remove_file(dev, &dev_attr_jitter);
+       device_remove_file(dev, &dev_attr_proximity_timeout);
        device_remove_file(dev, &dev_attr_delay);
        device_remove_file(dev, &dev_attr_input_path);
        device_remove_file(dev, &dev_attr_event_count);
@@ -1921,6 +2119,7 @@
        int ret;

        if ((ret = device_create_file(dev, &dev_attr_size)) ||
+           (ret = device_create_file(dev, &dev_attr_debug)) ||
            (ret = device_create_file(dev, &dev_attr_product_id)) ||
            (ret = device_create_file(dev, &dev_attr_vendor_id)) ||
            (ret = device_create_file(dev, &dev_attr_vendor)) ||
@@ -1931,6 +2130,7 @@
            (ret = device_create_file(dev, &dev_attr_xtilt)) ||
            (ret = device_create_file(dev, &dev_attr_ytilt)) ||
            (ret = device_create_file(dev, &dev_attr_jitter)) ||
+           (ret = device_create_file(dev, &dev_attr_proximity_timeout)) ||
            (ret = device_create_file(dev, &dev_attr_delay)) ||
            (ret = device_create_file(dev, &dev_attr_input_path)) ||
            (ret = device_create_file(dev, &dev_attr_event_count)) ||
@@ -1951,6 +2151,9 @@
        return ret;
 }

+/* forward declaration */
+static void activity_check(unsigned long data);
+
 /***********************************************************************
  * This routine is called when a tablet has been identified. It basically
  * sets up the tablet and the driver's internal structures.
@@ -1964,6 +2167,7 @@
        struct input_dev *inputdev;
        struct input_handle *inputhandle;
        struct list_head *node, *next;
+       //      char path[64 + 1];
        int i;
        int speeds[] = { 0,
                AIPTEK_PROGRAMMABLE_DELAY_50,
@@ -2002,6 +2206,8 @@
        aiptek->inDelay = 0;
        aiptek->endDelay = 0;
        aiptek->previousJitterable = 0;
+       aiptek->lastMacro = 0;
+

        /* Set up the curSettings struct. Said struct contains the current
         * programmable parameters. The newSetting struct contains changes
@@ -2021,10 +2227,58 @@
        aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON;
        aiptek->curSetting.jitterDelay = jitterDelay;
        aiptek->curSetting.programmableDelay = programmableDelay;
+       aiptek->curSetting.proximityTimeout = 0;

        /* Both structs should have equivalent settings
         */
-       aiptek->newSetting = aiptek->curSetting;
+       memcpy(&aiptek->newSetting, &aiptek->curSetting,
+              sizeof(struct aiptek_settings));
+
+       /* Now program the capacities of the tablet, in terms of being
+        * an input device.
+        */
+       set_bit(EV_KEY, aiptek->inputdev->evbit);
+       set_bit(EV_ABS, aiptek->inputdev->evbit);
+       set_bit(EV_REL, aiptek->inputdev->evbit);
+       set_bit(EV_MSC, aiptek->inputdev->evbit);
+
+       set_bit(ABS_X, aiptek->inputdev->absbit);
+       set_bit(ABS_Y, aiptek->inputdev->absbit);
+       set_bit(ABS_PRESSURE, aiptek->inputdev->absbit);
+       set_bit(ABS_TILT_X, aiptek->inputdev->absbit);
+       set_bit(ABS_TILT_Y, aiptek->inputdev->absbit);
+       set_bit(ABS_WHEEL, aiptek->inputdev->absbit);
+       set_bit(ABS_MISC, aiptek->inputdev->absbit);
+
+       set_bit(REL_X, aiptek->inputdev->relbit);
+       set_bit(REL_Y, aiptek->inputdev->relbit);
+       set_bit(REL_WHEEL, aiptek->inputdev->relbit);
+       set_bit(REL_MISC, aiptek->inputdev->relbit);
+
+       set_bit(BTN_LEFT, aiptek->inputdev->keybit);
+       set_bit(BTN_RIGHT, aiptek->inputdev->keybit);
+       set_bit(BTN_MIDDLE, aiptek->inputdev->keybit);
+
+       set_bit(BTN_TOOL_PEN, aiptek->inputdev->keybit);
+       set_bit(BTN_TOOL_RUBBER, aiptek->inputdev->keybit);
+       set_bit(BTN_TOOL_PENCIL, aiptek->inputdev->keybit);
+       set_bit(BTN_TOOL_AIRBRUSH, aiptek->inputdev->keybit);
+       set_bit(BTN_TOOL_BRUSH, aiptek->inputdev->keybit);
+       set_bit(BTN_TOOL_MOUSE, aiptek->inputdev->keybit);
+       set_bit(BTN_TOOL_LENS, aiptek->inputdev->keybit);
+       set_bit(BTN_TOUCH, aiptek->inputdev->keybit);
+       set_bit(BTN_STYLUS, aiptek->inputdev->keybit);
+       set_bit(BTN_STYLUS2, aiptek->inputdev->keybit);
+
+       set_bit(MSC_SERIAL, aiptek->inputdev->mscbit);
+
+       /* Programming the tablet macro keys needs to be done with a for loop
+        * as the keycodes are discontiguous.
+        */
+       for (i = 0; i < sizeof(macroKeyEvents) / sizeof(macroKeyEvents[0]);
+            ++i) {
+               set_bit(macroKeyEvents[i], aiptek->inputdev->keybit);
+       }

        /* Determine the usb devices' physical path.
         * Asketh not why we always pretend we're using "../input0",
@@ -2034,7 +2288,7 @@
         * us something...
         */
        usb_make_path(usbdev, aiptek->features.usbPath,
-                       sizeof(aiptek->features.usbPath));
+                      sizeof(aiptek->features.usbPath));
        strlcat(aiptek->features.usbPath, "/input0",
                sizeof(aiptek->features.usbPath));

@@ -2048,43 +2302,10 @@
        inputdev->private = aiptek;
        inputdev->open = aiptek_open;
        inputdev->close = aiptek_close;
+       aiptek->usbdev = usbdev;
+       aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber;

-       /* Now program the capacities of the tablet, in terms of being
-        * an input device.
-        */
-       inputdev->evbit[0] |= BIT(EV_KEY)
-           | BIT(EV_ABS)
-           | BIT(EV_REL)
-           | BIT(EV_MSC);
-
-       inputdev->absbit[0] |= BIT(ABS_MISC);
-
-       inputdev->relbit[0] |=
-           (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC));
-
-       inputdev->keybit[LONG(BTN_LEFT)] |=
-           (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE));
-
-       inputdev->keybit[LONG(BTN_DIGI)] |=
-           (BIT(BTN_TOOL_PEN) |
-            BIT(BTN_TOOL_RUBBER) |
-            BIT(BTN_TOOL_PENCIL) |
-            BIT(BTN_TOOL_AIRBRUSH) |
-            BIT(BTN_TOOL_BRUSH) |
-            BIT(BTN_TOOL_MOUSE) |
-            BIT(BTN_TOOL_LENS) |
-            BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2));
-
-       inputdev->mscbit[0] = BIT(MSC_SERIAL);
-
-       /* Programming the tablet macro keys needs to be done with a for loop
-        * as the keycodes are discontiguous.
-        */
-       for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
-               set_bit(macroKeyEvents[i], inputdev->keybit);
-
-       /*
-        * Program the input device coordinate capacities. We do not yet
+       /* Program the input device coordinate capacities. We do not yet
         * know what maximum X, Y, and Z values are, so we're putting fake
         * values in. Later, we'll ask the tablet to put in the correct
         * values.
@@ -2095,9 +2316,14 @@
        input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN,
AIPTEK_TILT_MAX, 0, 0);
        input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN,
AIPTEK_TILT_MAX, 0, 0);
        input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN,
AIPTEK_WHEEL_MAX - 1, 0, 0);
-
+       input_set_abs_params(inputdev, ABS_MISC, 0, 255, 0, 0);
        endpoint = &intf->altsetting[0].endpoint[0].desc;

+       /* inactivity timer. */
+       init_timer(&aiptek->activityCheck);
+       aiptek->activityCheck.function = activity_check;
+       aiptek->activityCheck.data = (unsigned long) aiptek;
+
        /* Go set up our URB, which is called when the tablet receives
         * input.
         */
@@ -2111,6 +2337,29 @@
        aiptek->urb->transfer_dma = aiptek->data_dma;
        aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

+       /* Register the tablet as an Input Device
+        */
+       input_register_device(aiptek->inputdev);
+
+       /* We now will look for the evdev device which is mapped to
+        * the tablet. The partial name is kept in the link list of
+        * input_handles associated with this input device.
+        * What identifies an evdev input_handler is that it begins
+        * with 'event', continues with a digit, and that in turn
+        * is mapped to /{devfs}/input/eventN.
+        */
+       inputdev = aiptek->inputdev;
+       list_for_each_safe(node, next, &inputdev->h_list) {
+               inputhandle = to_handle(node);
+               if (strncmp(inputhandle->name, "event", 5) == 0) {
+                       strcpy(aiptek->features.inputPath,
+                              inputhandle->name);
+                       break;
+               }
+       }
+
+       info("input: Aiptek as %s\n", aiptek->features.inputPath);
+
        /* Program the tablet. This sets the tablet up in the mode
         * specified in newSetting, and also queries the tablet's
         * physical capacities.
@@ -2131,24 +2380,12 @@
                        break;
                }
        }
-
-       /* Register the tablet as an Input Device
-        */
-       input_register_device(aiptek->inputdev);
-
-       /* We now will look for the evdev device which is mapped to
-        * the tablet. The partial name is kept in the link list of
-        * input_handles associated with this input device.
-        * What identifies an evdev input_handler is that it begins
-        * with 'event', continues with a digit, and that in turn
-        * is mapped to input/eventN.
-        */
-       list_for_each_safe(node, next, &inputdev->h_list) {
-               inputhandle = to_handle(node);
-               if (strncmp(inputhandle->name, "event", 5) == 0) {
-                       strcpy(aiptek->features.inputPath, inputhandle->name);
-                       break;
-               }
+
+       /* Murphy says that some day someone will have a tablet that fails the
+          above test. That's you, Frederic Rodrigo */
+       if (i == ARRAY_SIZE(speeds)) {
+               info("input: Aiptek tablet insane?");
+               goto fail2;
        }

        /* Associate this driver's struct with the usb interface.
@@ -2173,6 +2410,23 @@
        return -ENOMEM;
 }

+/* Activity checking thread. If a sufficient period of inactivity is detected,
+   the tablet's proximity is reset. */
+static void activity_check(unsigned long data)
+{
+       struct aiptek *aiptek = (struct aiptek *) data;
+
+       /* This timer is set *after* handling input for the table, and
+          cleared *before* handling it. Am guessing that we never run
+          concurrently the module code. */
+
+       /* info("aiptek: timeout proximity\n"); */
+
+       /* apparently over-due. Reset the misc key, no tool, no proximity */
+       input_report_abs(aiptek->inputdev, ABS_MISC, 0);
+       input_sync(aiptek->inputdev);
+}
+
 /* Forward declaration */
 static void aiptek_disconnect(struct usb_interface *intf);

@@ -2190,6 +2444,9 @@
 {
        struct aiptek *aiptek = usb_get_intfdata(intf);

+       /* First remove any timer. */
+       del_timer(&aiptek->activityCheck);
+
        /* Disassociate driver's struct with usb interface
         */
        usb_set_intfdata(intf, NULL);
@@ -2204,6 +2461,7 @@
                                AIPTEK_PACKET_LENGTH,
                                aiptek->data, aiptek->data_dma);
                kfree(aiptek);
+               aiptek = NULL;
        }
 }

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=so...
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel


Copyright © 2007, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds