Re: [Aiptektablet-users] [PATCH 2.6.20 1/1] usb/input updatedaiptek tablet driver

René van Paassen <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization TU Delft - Aerospace Engineering
Message-ID <[email protected]>
On Thu, 2007-03-08 at 00:02 +0100, Rene van Paassen wrote:
> 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 <[email protected]>

After feedback from usb list maintainers + some on the aiptek list, I
added the following changes to the patch:
- Removed product, vendor, product_id and vendor_id files, 
  these are already provided by input core--- - Got rid of accented UTF8
characters
- added error checking on input_register_device
- got rid of input path search
- using del_timer_sync to get rid of potential race on SMP
- fixes for relative mode, caught by Mark Vytlacil

Signed-off-by: René van Paassen <[email protected]>

--- 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-04-03 23:13:11.000000000 +0200
@@ -4,6 +4,7 @@
  *
  *  Copyright (c) 2001      Chris Atenasio   <[email protected]>
  *  Copyright (c) 2002-2004 Bryan W. Headley <[email protected]>
+ *  Extra bits    2004      Rene van Paassen <[email protected]> 
  *
  *  based on wacom.c by
  *     Vojtech Pavlik      <[email protected]>
@@ -45,6 +46,46 @@
  *             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
+ *      v2.2   After feedback from USB maintainers
+ *             - Removed product, vendor, product_id and vendor_id files, 
+ *               these are already provided by input core
+ *             - Got rid of accented UTF8 characters
+ *             - added error checking on input_register_device
+ *             - got rid of input path search
+ *             - using del_timer_sync to get rid of potential race on SMP
+ *             - fixes for relative mode, caught by Mark Vytlacil
  *
  * NOTE:
  *      This kernel driver is augmented by the "Aiptek" XFree86 input
@@ -77,14 +118,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.2 (Apr-03-2007)"
+#define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen"
 #define DRIVER_DESC    "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
 
 /*
@@ -113,7 +155,7 @@
  * (returned as Report 3 - absolute coordinates from the mouse)
  *
  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
- * byte0   0     0     0     0     0     0     1     0
+ * byte0   0     0     0     0     0     0     1     1
  * byte1  X7    X6    X5    X4    X3    X2    X1    X0
  * byte2  X15   X14   X13   X12   X11   X10   X9    X8
  * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
@@ -135,7 +177,7 @@
  * (returned as Report 5 - macrokeys from the mouse)
  *
  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
- * byte0   0     0     0     0     0     1     0     0
+ * byte0   0     0     0     0     0     1     0     1
  * byte1   0     0     0    BS2   BS    Tip   IR    DV
  * byte2   0     0     0     0     0     0     1     0
  * byte3   0     0     0    K4    K3    K2    K1    K0
@@ -265,9 +307,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 +329,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;
 
@@ -295,7 +343,6 @@
 	int modelCode;		/* Tablet model code (not unique) */
 	int firmwareCode;	/* prom/eeprom version            */
 	char usbPath[64 + 1];	/* device's physical usb path     */
-	char inputPath[64 + 1];	/* input device path              */
 };
 
 struct aiptek_settings {
@@ -312,9 +359,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 +377,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 +397,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 +513,21 @@
 	aiptek->inDelay = 0;
 	aiptek->eventCount++;
 
+	/* Now remove the inactivity timer, if it is still lurking around. */
+	if (aiptek->curSetting.proximityTimeout) {
+		del_timer_sync(&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) {
@@ -452,18 +544,32 @@
 			 * that a non-zero value indicates that one or more
 			 * mouse button was pressed.)
 			 */
-			jitterable = data[5] & 0x07;
+			jitterable = data[1] & 0x07;
 
-			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;
+			left = (data[1] & aiptek->curSetting.mouseButtonLeft>>2) != 0 ? 1 : 0;
+			right = (data[1] & aiptek->curSetting.mouseButtonRight>>2) != 0 ? 1 : 0;
+			middle = (data[1] & aiptek->curSetting.mouseButtonMiddle>>2) != 0 ? 1 : 0;
 
 			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 +579,9 @@
 						 aiptek->curSetting.wheel);
 				aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
 			}
+			input_report_key(inputdev,
+					 macroKeyEvents[aiptek->lastMacro],
+					 0);
 			input_sync(inputdev);
 		}
 	}
@@ -490,8 +599,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 +610,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 +629,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 +668,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 +690,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 +707,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 +730,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 +742,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 +760,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 +787,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 +804,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) {
+			} else {
 				input_report_key(inputdev,
-						 macroKeyEvents[macro - 1], 0);
-			}
-			if (macro < 25) {
-				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 +845,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 +859,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 +879,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 +994,9 @@
 		    command, data);
 	}
 	kfree(buf);
+
+	msleep(aiptek->curSetting.programmableDelay);
+
 	return ret < 0 ? ret : 0;
 }
 
@@ -972,6 +1097,7 @@
 			return ret;
 		}
 	}
+	msleep(160);
 
 	/* Enable the macro keys */
 	if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
@@ -1002,92 +1128,60 @@
  */
 
 /***********************************************************************
- * support the 'size' file -- display support
+ * Add dynamic debugging.
  */
-static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf)
+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, "%dx%d\n",
-			aiptek->inputdev->absmax[ABS_X] + 1,
-			aiptek->inputdev->absmax[ABS_Y] + 1);
-}
-
-/* These structs define the sysfs files, param #1 is the name of the
- * file, param 2 is the file permissions, param 3 & 4 are to the
- * output generator and input parser routines. Absence of a routine is
- * permitted -- it only means can't either 'cat' the file, or send data
- * to it.
- */
-static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
-
-/***********************************************************************
- * support routines for the 'product_id' file
- */
-static ssize_t show_tabletProductId(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, "0x%04x\n",
-			aiptek->inputdev->id.product);
+	return snprintf(buf, PAGE_SIZE, pInt, aiptek->debug);
 }
 
-static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL);
-
-/***********************************************************************
- * support routines for the 'vendor_id' file
- */
-static ssize_t show_tabletVendorId(struct device *dev, struct device_attribute *attr, char *buf)
+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)
+	if (aiptek == NULL) {
 		return 0;
+	}
 
-	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->inputdev->id.vendor);
+	aiptek->debug = (int) simple_strtol(buf, 0, 10);
+	return count;
 }
 
-static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL);
+static DEVICE_ATTR(debug, S_IRUGO | S_IWUGO, show_tabletDebug,
+		   store_tabletDebug);
 
 /***********************************************************************
- * support routines for the 'vendor' file
+ * support the 'size' file -- display support
  */
-static ssize_t show_tabletManufacturer(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct aiptek *aiptek = dev_get_drvdata(dev);
-	int retval;
 
 	if (aiptek == NULL)
 		return 0;
 
-	retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer);
-	return retval;
+	return snprintf(buf, PAGE_SIZE, "%dx%d\n",
+			aiptek->inputdev->absmax[ABS_X] + 1,
+			aiptek->inputdev->absmax[ABS_Y] + 1);
 }
 
-static DEVICE_ATTR(vendor, S_IRUGO, show_tabletManufacturer, NULL);
-
-/***********************************************************************
- * support routines for the 'product' file
+/* These structs define the sysfs files, param #1 is the name of the
+ * file, param 2 is the file permissions, param 3 & 4 are to the
+ * output generator and input parser routines. Absence of a routine is
+ * permitted -- it only means can't either 'cat' the file, or send data
+ * to it.
  */
-static ssize_t show_tabletProduct(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct aiptek *aiptek = dev_get_drvdata(dev);
-	int retval;
-
-	if (aiptek == NULL)
-		return 0;
-
-	retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product);
-	return retval;
-}
+static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
 
-static DEVICE_ATTR(product, S_IRUGO, show_tabletProduct, NULL);
 
 /***********************************************************************
  * support routines for the 'pointer_mode' file. Note that this file
@@ -1103,22 +1197,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 +1222,12 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "stylus") == 0) {
+	if (strncmp(buf, pStylus, ARRAY_SIZE(pStylus)) == 0) {
 		aiptek->newSetting.pointerMode =
 		    AIPTEK_POINTER_ONLY_STYLUS_MODE;
-	} else if (strcmp(buf, "mouse") == 0) {
+	} else if (strncmp(buf, pMouse, ARRAY_SIZE(pMouse)) == 0) {
 		aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE;
-	} else if (strcmp(buf, "either") == 0) {
+	} else if (strncmp(buf, pEither, ARRAY_SIZE(pEither)) == 0) {
 		aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
 	}
 	return count;
@@ -1157,18 +1251,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 +1272,11 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "absolute") == 0) {
-		aiptek->newSetting.pointerMode =
+	if (strncmp(buf, pAbsolute, ARRAY_SIZE(pAbsolute)) == 0) {
+		aiptek->newSetting.coordinateMode =
 		    AIPTEK_COORDINATE_ABSOLUTE_MODE;
-	} else if (strcmp(buf, "relative") == 0) {
-		aiptek->newSetting.pointerMode =
+	} else if (strncmp(buf, pRelative, ARRAY_SIZE(pRelative)) == 0) {
+		aiptek->newSetting.coordinateMode =
 		    AIPTEK_COORDINATE_RELATIVE_MODE;
 	}
 	return count;
@@ -1206,38 +1300,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 +1341,19 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "mouse") == 0) {
+	if (strncmp(buf, pMouse, ARRAY_SIZE(pMouse)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE;
-	} else if (strcmp(buf, "eraser") == 0) {
+	} else if (strncmp(buf, pEraser, ARRAY_SIZE(pEraser)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE;
-	} else if (strcmp(buf, "pencil") == 0) {
+	} else if (strncmp(buf, pPencil, ARRAY_SIZE(pPencil)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE;
-	} else if (strcmp(buf, "pen") == 0) {
+	} else if (strncmp(buf, pPen, ARRAY_SIZE(pPen)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
-	} else if (strcmp(buf, "brush") == 0) {
+	} else if (strncmp(buf, pBrush, ARRAY_SIZE(pBrush)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE;
-	} else if (strcmp(buf, "airbrush") == 0) {
+	} else if (strncmp(buf, pAirbrush, ARRAY_SIZE(pAirbrush)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE;
-	} else if (strcmp(buf, "lens") == 0) {
+	} else if (strncmp(buf, pLens, ARRAY_SIZE(pLens)) == 0) {
 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE;
 	}
 
@@ -1282,9 +1376,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 +1392,10 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "disable") == 0) {
+	if (strncmp(buf, pDisable, ARRAY_SIZE(pDisable)) == 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 +1418,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 +1434,10 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "disable") == 0) {
+	if (strncmp(buf, pDisable, ARRAY_SIZE(pDisable)) == 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 +1459,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,63 +1480,86 @@
 		   show_tabletJitterDelay, store_tabletJitterDelay);
 
 /***********************************************************************
- * support routines for the 'delay' file. Note that this file
+ * support routines for the proximity_timeout file. Note that this file
  * both displays current setting and allows reprogramming.
  */
-static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf)
+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, "%d\n",
-			aiptek->curSetting.programmableDelay);
+	return snprintf(buf, PAGE_SIZE, pInt,
+			(aiptek->curSetting.proximityTimeout * 1000) / HZ);
 }
 
 static ssize_t
-store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+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.programmableDelay = (int)simple_strtol(buf, NULL, 10);
+	aiptek->newSetting.proximityTimeout =
+	    ((int) simple_strtol(buf, 0, 10) * HZ) / 1000;
 	return count;
 }
 
-static DEVICE_ATTR(delay,
+static DEVICE_ATTR(proximity_timeout,
 		   S_IRUGO | S_IWUGO,
-		   show_tabletProgrammableDelay, store_tabletProgrammableDelay);
+		   show_tabletProximityTimeout,
+		   store_tabletProximityTimeout);
 
 /***********************************************************************
- * support routines for the 'input_path' file. Note that this file
- * only displays current setting.
+ * support routines for the 'delay' file. Note that this file
+ * both displays current setting and allows reprogramming.
  */
-static ssize_t show_tabletInputDevice(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t show_tabletProgrammableDelay(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, "/dev/input/%s\n",
-			aiptek->features.inputPath);
+	return snprintf(buf, PAGE_SIZE, pInt,
+			aiptek->curSetting.programmableDelay);
 }
 
-static DEVICE_ATTR(input_path, S_IRUGO, show_tabletInputDevice, NULL);
+static ssize_t
+store_tabletProgrammableDelay(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.programmableDelay = (int)simple_strtol(buf, 0, 10);
+	return count;
+}
+
+static DEVICE_ATTR(delay,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletProgrammableDelay, store_tabletProgrammableDelay);
 
 /***********************************************************************
  * 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 +1622,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 +1644,10 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "upper") == 0) {
+	if (strncmp(buf, pUpper, ARRAY_SIZE(pUpper)) == 0) {
 		aiptek->newSetting.stylusButtonUpper =
 		    AIPTEK_STYLUS_UPPER_BUTTON;
-	} else if (strcmp(buf, "lower") == 0) {
+	} else if (strncmp(buf, pLower, ARRAY_SIZE(pLower)) == 0) {
 		aiptek->newSetting.stylusButtonUpper =
 		    AIPTEK_STYLUS_LOWER_BUTTON;
 	}
@@ -1554,18 +1672,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 +1694,10 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "upper") == 0) {
+	if (strncmp(buf, pUpper, ARRAY_SIZE(pUpper)) == 0) {
 		aiptek->newSetting.stylusButtonLower =
 		    AIPTEK_STYLUS_UPPER_BUTTON;
-	} else if (strcmp(buf, "lower") == 0) {
+	} else if (strncmp(buf, pLower, ARRAY_SIZE(pLower)) == 0) {
 		aiptek->newSetting.stylusButtonLower =
 		    AIPTEK_STYLUS_LOWER_BUTTON;
 	}
@@ -1604,22 +1722,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 +1748,11 @@
 	if (aiptek == NULL)
 		return 0;
 
-	if (strcmp(buf, "left") == 0) {
+	if (strncmp(buf, pLeft, ARRAY_SIZE(pLeft)) == 0) {
 		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
-	} else if (strcmp(buf, "middle") == 0) {
+	} else if (strncmp(buf, pMiddle, ARRAY_SIZE(pMiddle)) == 0) {
 		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON;
-	} else if (strcmp(buf, "right") == 0) {
+	} else if (strncmp(buf, pRight, ARRAY_SIZE(pRight)) == 0) {
 		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON;
 	}
 	return count;
@@ -1658,22 +1776,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 +1802,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, ARRAY_SIZE(pLeft)) == 0) {
+		aiptek->newSetting.mouseButtonMiddle =
+		    AIPTEK_MOUSE_LEFT_BUTTON;
+	} else if (strncmp(buf, pMiddle, ARRAY_SIZE(pMiddle)) == 0) {
 		aiptek->newSetting.mouseButtonMiddle =
 		    AIPTEK_MOUSE_MIDDLE_BUTTON;
-	} else if (strcmp(buf, "right") == 0) {
+	} else if (strncmp(buf, pRight, ARRAY_SIZE(pRight)) == 0) {
 		aiptek->newSetting.mouseButtonMiddle =
 		    AIPTEK_MOUSE_RIGHT_BUTTON;
 	}
@@ -1701,7 +1820,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 +1833,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 +1859,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, ARRAY_SIZE(pLeft)) == 0) {
+		aiptek->newSetting.mouseButtonRight =
+		    AIPTEK_MOUSE_LEFT_BUTTON;
+	} else if (strncmp(buf, pMiddle, ARRAY_SIZE(pMiddle)) == 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, ARRAY_SIZE(pRight)) == 0) {
+		aiptek->newSetting.mouseButtonRight =
+		    AIPTEK_MOUSE_RIGHT_BUTTON;
 	}
 	return count;
 }
@@ -1767,9 +1888,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 +1903,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 +1942,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 +1964,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 +1980,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 +1996,7 @@
 	if (aiptek == NULL)
 		return 0;
 
-	return snprintf(buf, PAGE_SIZE, "%04x\n",
+	return snprintf(buf, PAGE_SIZE, pHex,
 			aiptek->features.firmwareCode);
 }
 
@@ -1885,19 +2008,16 @@
  */
 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);
-	device_remove_file(dev, &dev_attr_vendor);
-	device_remove_file(dev, &dev_attr_product);
 	device_remove_file(dev, &dev_attr_pointer_mode);
 	device_remove_file(dev, &dev_attr_coordinate_mode);
 	device_remove_file(dev, &dev_attr_tool_mode);
 	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);
 	device_remove_file(dev, &dev_attr_diagnostic);
 	device_remove_file(dev, &dev_attr_odm_code);
@@ -1921,18 +2041,15 @@
 	int ret;
 
 	if ((ret = device_create_file(dev, &dev_attr_size)) ||
-	    (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)) ||
-	    (ret = device_create_file(dev, &dev_attr_product)) ||
+	    (ret = device_create_file(dev, &dev_attr_debug)) ||
 	    (ret = device_create_file(dev, &dev_attr_pointer_mode)) ||
 	    (ret = device_create_file(dev, &dev_attr_coordinate_mode)) ||
 	    (ret = device_create_file(dev, &dev_attr_tool_mode)) ||
 	    (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)) ||
 	    (ret = device_create_file(dev, &dev_attr_diagnostic)) ||
 	    (ret = device_create_file(dev, &dev_attr_odm_code)) ||
@@ -1951,6 +2068,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.
@@ -1962,8 +2082,7 @@
 	struct usb_endpoint_descriptor *endpoint;
 	struct aiptek *aiptek;
 	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 +2121,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 +2142,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 +2203,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 +2217,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 +2231,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 +2252,14 @@
 	aiptek->urb->transfer_dma = aiptek->data_dma;
 	aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
+	/* Register the tablet as an Input Device
+	 */
+	i = input_register_device(aiptek->inputdev);
+	if (i) {
+		info("aiptek: input_register_device returned err: %d", i);
+		goto fail2;
+	}
+
 	/* Program the tablet. This sets the tablet up in the mode
 	 * specified in newSetting, and also queries the tablet's
 	 * physical capacities.
@@ -2131,24 +2280,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 +2310,21 @@
 	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. */
+
+	/* 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 +2342,9 @@
 {
 	struct aiptek *aiptek = usb_get_intfdata(intf);
 
+	/* First remove any timer. */
+	del_timer_sync(&aiptek->activityCheck);
+
 	/* Disassociate driver's struct with usb interface
 	 */
 	usb_set_intfdata(intf, NULL);
@@ -2204,6 +2359,7 @@
 				AIPTEK_PACKET_LENGTH,
 				aiptek->data, aiptek->data_dma);
 		kfree(aiptek);
+		aiptek = NULL;
 	}
 }
 

###########################################

This message has been scanned by F-Secure Anti-Virus for Microsoft Exchange.
For more information, connect to http://www.f-secure.com/

-------------------------------------------------------------------------
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=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.