ChangeSet 1.1261, 2003/06/18 17:07:07-07:00, [EMAIL PROTECTED]

[PATCH] USB: Aiptek kernel driver 1.0 for Kernel 2.4


 Documentation/Configure.help |    9 
 MAINTAINERS                  |    7 
 drivers/usb/aiptek.c         | 1200 +++++++++++++++++++++++++++++++++++++++----
 drivers/usb/hid-core.c       |   16 
 4 files changed, 1139 insertions(+), 93 deletions(-)


diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help
--- a/Documentation/Configure.help      Wed Jun 18 17:34:45 2003
+++ b/Documentation/Configure.help      Wed Jun 18 17:34:45 2003
@@ -13919,11 +13919,12 @@
   The module will be called powermate.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
-Aiptek 6000U/8000U tablet support
+Aiptek HyperPen tablet support
 CONFIG_USB_AIPTEK
-  Say Y here if you want to use the USB version of the Aiptek 6000U/8000U
-  tablet.  Make sure to say Y to "Event interface support"
-  (CONFIG_INPUT_EVDEV) as well.
+  Say Y here if you want to use the USB version of the Aiptek HyperPen
+  Digital Tablet (models 4000U, 5000U, 6000U, 8000U, and 12000U.)
+  Make sure to say Y to "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or
+  "Event interface support" (CONFIG_INPUT_EVDEV) as well.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
diff -Nru a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS       Wed Jun 18 17:34:45 2003
+++ b/MAINTAINERS       Wed Jun 18 17:34:45 2003
@@ -214,6 +214,13 @@
 W:     http://www.linux-usb.org/SpeedTouch/
 S:     Maintained
 
+AIPTEK USB TABLET DRIVER
+P:     Bryan W. Headley
+M:     Bryan W. Headley <[EMAIL PROTECTED]>
+W:     http://aiptektablet.sourceforge.net
+L:     [EMAIL PROTECTED]
+S:     Maintained
+
 ALTERA EPXA1/EPXA10 DEVELOPMENT BOARD PORT
 P:     Clive Davies
 M:     [EMAIL PROTECTED]
diff -Nru a/drivers/usb/aiptek.c b/drivers/usb/aiptek.c
--- a/drivers/usb/aiptek.c      Wed Jun 18 17:34:45 2003
+++ b/drivers/usb/aiptek.c      Wed Jun 18 17:34:45 2003
@@ -1,7 +1,9 @@
 /*
- *  Native support for the Aiptek 8000U
+ *  Native support for the Aiptek HyperPen USB Tablets
+ *  (4000U/5000U/6000U/8000U/12000U)
  *
- *  Copyright (c) 2001 Chris Atenasio          <[EMAIL PROTECTED]>
+ *  Copyright (c) 2001      Chris Atenasio   <[EMAIL PROTECTED]>
+ *  Copyright (c) 2002-2003 Bryan W. Headley <[EMAIL PROTECTED]>
  *
  *  based on wacom.c by
  *     Vojtech Pavlik      <[EMAIL PROTECTED]>
@@ -11,13 +13,28 @@
  *     James E. Blair      <[EMAIL PROTECTED]>
  *     Daniel Egger        <[EMAIL PROTECTED]>
  *
- *
  *  Many thanks to Oliver Kuechemann for his support.
  *
  *  ChangeLog:
  *      v0.1 - Initial release
- *      v0.2 - Hack to get around fake event 28's.
+ *      v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
  *      v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
+ *             Released to Linux 2.4.19 and 2.5.x
+ *      v0.4 - Rewrote substantial portions of the code to deal with
+ *             corrected control sequences, timing, dynamic configuration,
+ *             support of 6000U - 12000U, procfs, and macro key support
+ *             (Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
+ *      v1.0 - Added support for diagnostic messages, count of messages
+ *             received from URB - Mar-8-2003, Bryan W. Headley
+ *
+ * NOTE:
+ *      This kernel driver is augmented by the "Aiptek" XFree86 input
+ *      driver for your X server, as well as a GUI Front-end "Tablet Manager".
+ *      These three products are highly interactive with one another, 
+ *      so therefore it's easier to document them all as one subsystem.
+ *      Please visit the project's "home page", located at, 
+ *      http://aiptektablet.sourceforge.net.
+ *
  */
 
 /*
@@ -42,13 +59,15 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/usb.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.3"
-#define DRIVER_AUTHOR "Chris Atenasio <[EMAIL PROTECTED]>"
-#define DRIVER_DESC "USB Aiptek 6000U/8000U tablet driver (Linux 2.4.x)"
+#define DRIVER_VERSION "v1.0 Mar-8-2003"
+#define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio"
+#define DRIVER_DESC    "Aiptek HyperPen USB Tablet Driver (Linux 2.4.x)"
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
@@ -56,6 +75,7 @@
 
 /*
  * Aiptek status packet:
+ * (returned as Report 1)
  *
  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
  * byte0   0     0     0     0     0     0     1     0
@@ -69,10 +89,14 @@
  *
  * IR: In Range = Proximity on
  * DV = Data Valid
+ * BS = Barrel Switch (as in, macro keys)
+ * BS2 also referred to as Tablet Pick
  *
- * 
  * Command Summary:
  *
+ * Use report_type CONTROL (3)
+ * Use report_id   2
+ *
  * Command/Data    Description     Return Bytes    Return Value
  * 0x10/0x00       SwitchToMouse       0
  * 0x10/0x01       SwitchToTablet      0
@@ -85,30 +109,278 @@
  * 0x03/0x00       GetODMCode          2           ODMCode
  * 0x08/0x00       GetPressureLevels   2           =512
  * 0x04/0x00       GetFirmwareVersion  2           Firmware Version
+ * 0x11/0x02       EnableMacroKeys     0
  *
  *
  * To initialize the tablet:
  *
- * (1) Send command Resolution500LPI
- * (2) Option Commands (GetXExtension, GetYExtension)
- * (3) Send command SwitchToTablet
+ * (1) Send Resolution500LPI (Command)
+ * (2) Query for Model code (Option Report)
+ * (3) Query for ODM code (Option Report)
+ * (4) Query for firmware (Option Report)
+ * (5) Query for GetXExtension (Option Report)
+ * (6) Query for GetYExtension (Option Report)
+ * (7) Query for GetPressureLevels (Option Report)
+ * (8) SwitchToTablet for Absolute coordinates, or
+ *     SwitchToMouse for Relative coordinates (Command)
+ * (9) EnableMacroKeys (Command)
+ * (10) FilterOn (Command)
+ * (11) AutoGainOn (Command)
+ *
+ * (Step 9 can be omitted, but you'll then have no function keys.)
+ *
+ *  The procfs interface
+ *  --------------------
+ *
+ *  This driver supports delivering configuration/status reports
+ *  through {procfs}/driver/usb/aiptek. ("procfs" is normally mounted
+ *  to /proc.) Said file can be found while the driver is active in
+ *  memory; it will be removed when the driver is removed, either 
+ *  through user intervention (rmmod aiptek) or through software
+ *  such as "hotplug".
+ *
+ *  Reading from the Procfs interface
+ *  ---------------------------------
+ *
+ *  The user may determine the status of the tablet by reading the
+ *  report in the procfs interface, /proc/driver/usb/aiptek.
+ *  The report as of driver version 1.0, looks like,
+ *
+ * Aiptek Tablet (3000x2250, 8.00x6.00", 202x152mm)
+ * (USB VendorID 0x08ca, ProductID 0x0020, ODMCode 0x0004
+ *  ModelCode: 0x64, FirmwareCode: 0x0400)
+ * on /dev/input/event0
+ * pointer=either
+ * coordinate=absolute
+ * tool=pen
+ * xtilt=disable
+ * ytilt=disable
+ * jitter=50
+ * diagnostic=none
+ * eventsReceived=0
+ *
+ *  (spurious ", for the benefit of vim's syntax highlighting.)
+ *
+ *  This report indicates the tablet recognized. (Because Aiptek reuses
+ *  the USB 'productID' over several tablets, it's pointless for us to
+ *  guess which model you have: we'll instead tell you the size of
+ *  the tablet's drawing area, which we indicate in coordinates, inches,
+ *  and millimeters.) We also indicate datum read from the USB interface,
+ *  such as vendorId, productId, ODMcode, etc. It's there "just in case."
+ *
+ *      on /dev/input/event0
+ *
+ *  Linux supports HID-compliant USB devices (such as this tablet) by
+ *  transposing their reports to the Linux Input Event System format. Which
+ *  means, if you want to data from the tablet, that's where it will be
+ *  made available from. For information on the Input Event System, see
+ *  the docs in ./Documentation/input, in the kernel source tree.
+ *
+ *  And yes, depending on the order in which other supported Input Event
+ *  devices are recognized and configured, the tablet may be allocated
+ *  to a different device driver name: it's all dynamic. Use of the devfs
+ *  file system is a help.
+ *
+ *  The keyword=value part of the report mostly shows what the programmable
+ *  parameters have been set to. We describe those below, and how to
+ *  program/reprogram them. Note: tablet parameters are to be programmed
+ *  while the tablet is attached and active. They are not set as arguments
+ *  to the kernel during bootup.
+ *
+ *  Here are the "read-only" parameters, and what they mean:
+ *
+ *      diagnostic=stringValue
+ *      eventsReceived=numericValue
+ *
+ * diagnostic: The tablet driver attempts to explain why things are not
+ *      working correctly. (To the best of it's insular abilities)
+ *
+ *      By default, the tablet boots up in Relative Coordinate
+ *      mode. This driver initially attempts to program it in Absolute
+ *      Coordinate mode (and of course, the user can subsequently choose
+ *      which mode they want.) So, therefore, the situation can arise
+ *      where the tablet is in one mode, and the driver believes it
+ *      is in the other mode. The driver, however, cannot divine
+ *      this mismatch until input events are received.  
+ *      Two reports indicate such mode-mismatches between the tablet
+ *      and the driver, and are,
+ *
+ *          "tablet sending relative reports"
+ *          "tablet sending absolute reports"
+ *
+ *      The next diagnostic operates in conjunction with the "pointer="
+ *      programmable parameter. With it, you can indicate that you want
+ *      the tablet to only accept reports from the stylus, or only from the 
+ *      mouse. (You can also specify to allow reports from either.) What
+ *      happens when you specify that you only want mouse reports, yet
+ *      the tablet keeps receiving reports from the stylus? Well, first,
+ *      it's a "pilot error", but secondly, it tries to diagnose the issue
+ *      with the following reports,
+ *
+ *          "tablet seeing reports from stylus"
+ *          "tablet seeing reports from mouse"
+ *
+ *      What if there is nothing to report? The inference in the diagnostic
+ *      reports is that something is happening which shouldn't: when things
+ *      appear to be working right, the report is,
+ *
+ *          "none"
+ *
+ *      The error diagnostic report is dynamic: it only reports issues
+ *      that are happening, or have happened as of the last event received.
+ *      It will reset following any attempt to reprogram the tablet's mode.
+ *
+ * eventsReceived: Occasionally, your movements on the tablet are not being
+ *      reported. Usually, this indicates that your tablet is out of sync
+ *      with the USB interface driver, or itself is not sending reports
+ *      out. To help diagnose this, we keep an active count of events
+ *      received from the tablet. So, if you move the stylus, and yet 
+ *      your client application doesn't notice, make
+ *      note of the eventsReceived, and then move the stylus again. If the
+ *      event counter's number doesn't change, then the tablet indeed has
+ *      "froze". 
+ *
+ *      We have found that sending the tablet a command sequence often
+ *      will clear up "frozen" tablets. Which segues into the section
+ *      about how to program your tablet through the procfs interface,
+ *
+ *  Writing to the procfs interface
+ *  -------------------------------
+ *
+ *  The user may configure the tablet by writing ASCII
+ *  commands to the /proc/driver/usb/aiptek file. Commands which are
+ *  accepted are,
+ *
+ *      pointer=stringvalue      {stylus|mouse|either}
+ *      coordinate=stringvalue   {absolute|relative}
+ *      tool=stringvalue         {mouse|rubber|pen|pencil|brush|airbrush}
+ *      xtilt=string_or_numeric  {disable|[-128..127]}
+ *      ytilt=string_or_numeric  {disable|[-128..127]}
+ *      jitter=numericvalue      {0..xxx}
+ *
+ *  pointer: you can specify that reports are to be excepted ONLY from the
+ *      stylus, or ONLY from the mouse. 'either' allows reports from either
+ *      device to be accepted, and is the default.
+ *  coordinate: you can specify that either absolute or relative coordinate
+ *      reports are issued by the tablet. By default, absolute reports are
+ *      sent.
+ *  tool: The stylus by default prepends TOOL_BTN_PEN events with it's
+ *      reports. But you may decide that you want your stylus to behave
+ *      like an eraser (named 'rubber', following tablet conventions,)
+ *      or a pencil, etc. The behavior is dependent upon the client software
+ *      consuming the tablet's events, e.g., the XFree86 tablet driver.
+ *  xtilt: By default this is disabled. However, other tablets have a notion
+ *      of measuring the angle at which the stylus pen is held against the
+ *      drawing surface, along the X axis. Aiptek tablets cannot sense this,
+ *      but if you want to send "held-at-angle" reports, specify the value,
+ *      an integer between -128 and 127 (inclusive) that you want to send.
+ *      This data will be sent along with regular tablet input. Obviously,
+ *      the inference here is that your hand does not change angles 
+ *      while drawing (until you go back to this procfs interface, and
+ *      change the value)!
+ *
+ *      When you consider actual drawing tools (real pens, brushes),
+ *      knowing the tools' tip shape and the angle that you hold the tool 
+ *      becomes important, insofar as calculating the surface of the tip 
+ *      that actually touches the surface of the paper. Knowledge of what 
+ *      to do with xtilt reports is solely in the realm of your client 
+ *      software.
+ *
+ *      Yes, there is a difference between xtilt=0 and xtilt=disable
+ *      settings. The former sends a report that the angle is a 0;
+ *      the other indicates that NO xtilt reports are to be sent at all.
+ *  ytilt: By default this is disabled. This provides similar functionality
+ *      to xtilt, except that we're measuring the angle the stylus pen is
+ *      held against the drawing surface, along the Y axis. Same cavaets
+ *      apply as for xtilt.
+ *  jitter: By default, this is set to 50. When pressing a button on
+ *      either the mouse or the stylus pen, you will probably notice that
+ *      the tool moves slightly from it's original position, until your
+ *      hand steadies it. During that period of time, the pen is "jittering",
+ *      sending spurious movement events that perhaps you'd like it not to
+ *      send. What we do is set a moratorium, measured in milliseconds,
+ *      during which we do not send movement events. So, the default is 50ms;
+ *      you obviously can set it to zero or incredibly unreasonable values
+ *      (no reports for 4 seconds following the pressing of a stylus button!)
+ *
+ * Interesting Side-Note
+ * ---------------------
+ *
+ *  The tablet has "frozen" and you'd like to send it a command to wake it
+ *  up. But you don't want to change how the driver's currently configured.
+ *
+ *  1. Send a command to /proc/driver/usb/aiptek with the same setting
+ *     already reported by the driver.
+ *  2. Send an illegal string to procfs file ("wakeup=now" is always good)
+ *  3. Because, the driver always attempts to reprogram the tablet to it's
+ *     current settings following a write to the procfs interface.
+ *
+ *  Hmm, still does not work.
+ *  -------------------------
+ *
+ *  This is slightly harder to diagnose. You may be receiving frame errors
+ *  from the USB interface driver (see /var/log/messages for any diagnostics).
+ *
+ *  Alternatively, you may be running something like 'hotplug' that attempts
+ *  to match discovered USB devices to it's list of device drivers. 
+ *  Unfortunately, because this is a tablet that can send relative X,Y events,
+ *  it "looks like" a mouse! A usb mouse driver may have possession of
+ *  input from the tablet. On the other hand, the tablet also supports 
+ *  absolute reports from barrel switches, which sounds a lot like a "joystick",
+ *  and the software again can be fooled into loading the wrong driver for
+ *  the tablet. The distinction is, USB HID devices tell you what they
+ *  are capable of, rather than what they are.
+ *
+ *  Come visit this driver's home page at http://aiptektablet.sourceforge.net
+ *  for further assistance.
  */
 
 #define USB_VENDOR_ID_AIPTEK   0x08ca
 
+#define AIPTEK_POINTER_ONLY_MOUSE_MODE      0
+#define AIPTEK_POINTER_ONLY_STYLUS_MODE     1
+#define AIPTEK_POINTER_EITHER_MODE          2
+
+#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \
+        (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \
+         a == AIPTEK_POINTER_EITHER_MODE)
+#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \
+        (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \
+         a == AIPTEK_POINTER_EITHER_MODE)
+
+#define AIPTEK_COORDINATE_RELATIVE_MODE 0
+#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1
+
+#define AIPTEK_TILT_MIN                      (-128)
+#define AIPTEK_TILT_MAX                      127
+#define AIPTEK_TILT_DISABLE                  (-10101)
+
+#define AIPTEK_TOOL_BUTTON_PEN_MODE          0
+#define AIPTEK_TOOL_BUTTON_PENCIL_MODE       1
+#define AIPTEK_TOOL_BUTTON_BRUSH_MODE        2
+#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE     3
+#define AIPTEK_TOOL_BUTTON_RUBBER_MODE       4
+#define AIPTEK_TOOL_BUTTON_MOUSE_MODE        5
+
+#define AIPTEK_DIAGNOSTIC_NA                 0
+#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE  1
+#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE  2
+#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED               3
+
+    // Time to wait (in ms) to help mask hand jittering
+    // when pressing the stylus buttons.
+#define AIPTEK_JITTER_DELAY_DEFAULT         50
+
 struct aiptek_features {
        char *name;
        int pktlen;
        int x_max;
        int y_max;
-       int pressure_min;
        int pressure_max;
+       int odmCode;
+       int modelCode;
+       int firmwareCode;
        void (*irq) (struct urb * urb);
-       unsigned long evbit;
-       unsigned long absbit;
-       unsigned long relbit;
-       unsigned long btnbit;
-       unsigned long digibit;
 };
 
 struct aiptek {
@@ -117,59 +389,381 @@
        struct usb_device *usbdev;
        struct urb *irq;
        struct aiptek_features *features;
-       int tool;
-       int open;
+       unsigned int ifnum;
+       int open_count;
+       int pointer_mode;
+       int coordinate_mode;
+       int tool_mode;
+       int xTilt;
+       int yTilt;
+       int diagnostic;
+       unsigned long eventCount;
+       int jitterDelay;
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *usbProcfsEntry;
+       struct proc_dir_entry *aiptekProcfsEntry;
+#endif
 };
 
+/*
+ * Permit easy lookup of keyboard events to send, versus
+ * the bitmap which comes from the tablet. This hides the
+ * issue that the F_keys are not sequentially numbered.
+ */
+static int macroKeyEvents[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
+       KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,
+       KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18,
+       KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24,
+       KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, KEY_FRONT, KEY_COPY,
+       KEY_OPEN, KEY_PASTE, 0
+};
+
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry *proc_root_driver;
+#endif
+
+static int
+aiptek_convert_from_2s_complement(unsigned char c)
+{
+       unsigned char b = c;
+       int negate = 0;
+       if (b & 0x80) {
+               b = ~b;
+               b--;
+               negate = 1;
+       }
+       int ret = b;
+       ret = (negate == 1) ? -ret : ret;
+       return ret;
+}
+
+/*
+ * aiptek_irq can receive one of six potential reports.
+ * The documentation for each is in the body of the function.
+ *
+ * The tablet reports on several attributes per invocation of
+ * aiptek_irq. Because the Linux Input Event system allows the
+ * transmission of ONE attribute per input_report_xxx() call,
+ * collation has to be done on the other end to reconstitute
+ * a complete tablet report. Further, the number of Input Event reports
+ * submitted varies, depending on what USB report type, and circumstance.
+ * To deal with this, EV_MSC is used to indicate an 'end-of-report'
+ * message. This has been an undocumented convention understood by the kernel
+ * tablet driver and clients such as gpm and XFree86's tablet drivers.
+ *
+ * Of the information received from the tablet, the one piece I
+ * cannot transmit is the proximity bit (without resorting to an EV_MSC
+ * convention above.) I therefore have taken over REL_MISC and ABS_MISC
+ * (for relative and absolute reports, respectively) for communicating
+ * Proximity. Why two events? I thought it interesting to know if the
+ * Proximity event occured while the tablet was in absolute or relative
+ * mode.
+ *
+ * Other tablets use the notion of a certain minimum stylus pressure
+ * to infer proximity. While that could have been done, that is yet
+ * another 'by convention' behavior, the documentation for which
+ * would be spread between two (or more) pieces of software.
+ *
+ * EV_MSC usage is terminated in Linux 2.5.x.
+ */
+
 static void
 aiptek_irq(struct urb *urb)
 {
        struct aiptek *aiptek = urb->context;
        unsigned char *data = aiptek->data;
        struct input_dev *dev = &aiptek->dev;
-       int x;
-       int y;
-       int pressure;
-       int proximity;
+       int jitterable = 0;
 
        if (urb->status)
                return;
 
-       if ((data[0] & 2) == 0) {
-               dbg("received unknown report #%d", data[0]);
-       }
-
-       proximity = data[5] & 0x01;
-       input_report_key(dev, BTN_TOOL_PEN, proximity);
+       aiptek->eventCount++;
 
-       x = ((__u32) data[1]) | ((__u32) data[2] << 8);
-       y = ((__u32) data[3]) | ((__u32) data[4] << 8);
-       pressure = ((__u32) data[6]) | ((__u32) data[7] << 8);
-       pressure -= aiptek->features->pressure_min;
+       // Report 1 delivers relative coordinates with either a stylus
+       // or the mouse. You do not know which tool generated the event.
+       if (data[0] == 1) {
+               if (aiptek->coordinate_mode == AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+                       aiptek->diagnostic =
+                           AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE;
+               } else {
+                       if (aiptek->tool_mode != AIPTEK_TOOL_BUTTON_MOUSE_MODE) {
+                               aiptek->tool_mode =
+                                   AIPTEK_TOOL_BUTTON_MOUSE_MODE;
+                               input_report_key(dev, BTN_TOOL_MOUSE, 1);
+                       }
+                       int x = aiptek_convert_from_2s_complement(data[2]);
+                       int y = aiptek_convert_from_2s_complement(data[3]);
+
+                       int left = data[5] & 0x01;
+                       int right = data[5] & 0x02;
+                       int middle = data[5] & 0x04;
+
+                       jitterable = left | right | middle;
+
+                       input_report_key(dev, BTN_LEFT, left);
+                       input_report_key(dev, BTN_MIDDLE, middle);
+                       input_report_key(dev, BTN_RIGHT, right);
+                       input_report_rel(dev, REL_X, x);
+                       input_report_rel(dev, REL_Y, y);
+                       input_report_rel(dev, REL_MISC, 1);
 
-       if (pressure < 0) {
-               pressure = 0;
+                       input_event(dev, EV_MSC, MSC_SERIAL, 0);
+               }
        }
-
-       if (proximity) {
-               input_report_abs(dev, ABS_X, x);
-               input_report_abs(dev, ABS_Y, y);
-               input_report_abs(dev, ABS_PRESSURE, pressure);
-               input_report_key(dev, BTN_TOUCH, data[5] & 0x04);
-               input_report_key(dev, BTN_STYLUS, data[5] & 0x08);
-               input_report_key(dev, BTN_STYLUS2, data[5] & 0x10);
+       // Report 2 is delivered only by the stylus, and delivers
+       // absolute coordinates.
+       else if (data[0] == 2) {
+               if (aiptek->coordinate_mode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+                       aiptek->diagnostic =
+                           AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+               } else
+                   if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE(aiptek->pointer_mode))
+               {
+                       aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+               } else {
+                       int x = ((__u32) data[1]) | ((__u32) data[2] << 8);
+                       int y = ((__u32) data[3]) | ((__u32) data[4] << 8);
+                       int z = ((__u32) data[6]) | ((__u32) data[7] << 8);
+
+                       int p = data[5] & 0x01;
+                       int dv = data[5] & 0x02;
+                       int tip = data[5] & 0x04;
+                       int bs = data[5] & 0x08;
+                       int pck = data[5] & 0x10;
+
+                       // dv indicates 'data valid' (e.g., the tablet is in sync
+                       // and has delivered a "correct" report) We will ignore
+                       // all 'bad' reports...
+                       if (dv != 0) {
+                               switch (aiptek->tool_mode) {
+                               case AIPTEK_TOOL_BUTTON_PEN_MODE:
+                                       {
+                                               input_report_key(dev,
+                                                                BTN_TOOL_PEN,
+                                                                1);
+                                       }
+                                       break;
+
+                               case AIPTEK_TOOL_BUTTON_PENCIL_MODE:
+                                       {
+                                               input_report_key(dev,
+                                                                BTN_TOOL_PENCIL,
+                                                                1);
+                                       }
+                                       break;
+
+                               case AIPTEK_TOOL_BUTTON_BRUSH_MODE:
+                                       {
+                                               input_report_key(dev,
+                                                                BTN_TOOL_BRUSH,
+                                                                1);
+                                       }
+                                       break;
+
+                               case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE:
+                                       {
+                                               input_report_key(dev,
+                                                                BTN_TOOL_AIRBRUSH,
+                                                                1);
+                                       }
+                                       break;
+
+                               case AIPTEK_TOOL_BUTTON_RUBBER_MODE:
+                                       {
+                                               input_report_key(dev,
+                                                                BTN_TOOL_RUBBER,
+                                                                1);
+                                       }
+                                       break;
+
+                               case AIPTEK_TOOL_BUTTON_MOUSE_MODE:
+                                       {
+                                               input_report_key(dev,
+                                                                BTN_TOOL_MOUSE,
+                                                                1);
+                                       }
+                                       break;
+                               }
+
+                               input_report_abs(dev, ABS_X, x);
+                               input_report_abs(dev, ABS_Y, y);
+
+                               /*
+                                * The user is allowed to switch from one of the
+                                * stylus tools to the Mouse using the front-end GUI.
+                                * An issue that will arise, however, is what happens
+                                * when the user HAS issued a TOOL_BTN_MOUSE, but has 
not
+                                * yet swapped tools. Well, we can "pretend" to be a 
mouse
+                                * by sending overriding tip, barrelswitch and pick.
+                                * This stupidity should not be used as an excuse not
+                                * to physically move your Aiptek mouse into the 
tablet's
+                                * active area -- it merely provides momentary 
convenience
+                                * during that transition.
+                                */
+                               if (aiptek->tool_mode ==
+                                   AIPTEK_TOOL_BUTTON_MOUSE_MODE) {
+                                       input_report_key(dev, BTN_LEFT, tip);
+                                       input_report_key(dev, BTN_RIGHT, bs);
+                                       input_report_key(dev, BTN_MIDDLE, pck);
+
+                                       jitterable = tip | bs | pck;
+                               } else {
+                                       input_report_abs(dev, ABS_PRESSURE, z);
+
+                                       input_report_key(dev, BTN_TOUCH, tip);
+                                       input_report_key(dev, BTN_STYLUS, bs);
+                                       input_report_key(dev, BTN_STYLUS2, pck);
+
+                                       jitterable = tip | bs | pck;
+
+                                       if (aiptek->xTilt !=
+                                           AIPTEK_TILT_DISABLE)
+                                               input_report_abs(dev,
+                                                                ABS_TILT_X,
+                                                                aiptek->xTilt);
+                                       if (aiptek->yTilt !=
+                                           AIPTEK_TILT_DISABLE)
+                                               input_report_abs(dev,
+                                                                ABS_TILT_Y,
+                                                                aiptek->yTilt);
+                               }
+                               input_report_abs(dev, ABS_MISC, p);
+                               input_event(dev, EV_MSC, MSC_SERIAL, 0);
+                       }
+               }
+       }
+       // Report 3's come from the mouse in absolute mode.
+       else if (data[0] == 3) {
+               if (aiptek->coordinate_mode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+                       aiptek->diagnostic =
+                           AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+               } else
+                   if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE(aiptek->pointer_mode))
+               {
+                       aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+               } else {
+                       int x = ((__u32) data[1]) | ((__u32) data[2] << 8);
+                       int y = ((__u32) data[3]) | ((__u32) data[4] << 8);
+                       int p = data[5] & 0x01;
+                       int dv = data[5] & 0x02;
+                       int left = data[5] & 0x04;
+                       int right = data[5] & 0x08;
+                       int middle = data[5] & 0x10;
+
+                       if (dv != 0) {
+                               input_report_key(dev, BTN_TOOL_MOUSE, 1);
+                               input_report_abs(dev, ABS_X, x);
+                               input_report_abs(dev, ABS_Y, y);
+
+                               input_report_key(dev, BTN_LEFT, left);
+                               input_report_key(dev, BTN_MIDDLE, middle);
+                               input_report_key(dev, BTN_RIGHT, right);
+
+                               jitterable = left | middle | right;
+
+                               input_report_rel(dev, REL_MISC, p);
+                               input_event(dev, EV_MSC, MSC_SERIAL, 0);
+                       }
+               }
+       }
+       // Report 4s come from the macro keys when pressed by stylus
+       else if (data[0] == 4) {
+               int p = data[1] & 0x01;
+               int dv = data[1] & 0x02;
+               int tip = data[1] & 0x04;
+               int bs = data[1] & 0x08;
+               int pck = data[1] & 0x10;
+
+               int m = data[3];
+               int z = ((__u32) data[4]) | ((__u32) data[5] << 8);
+
+               if (dv != 0) {
+                       input_report_key(dev, BTN_TOUCH, tip);
+                       input_report_key(dev, BTN_STYLUS, bs);
+                       input_report_key(dev, BTN_STYLUS2, pck);
+
+                       jitterable = tip | bs | pck;
+
+                       input_report_key(dev, macroKeyEvents[m - 1], 1);
+                       input_report_abs(dev, ABS_PRESSURE, z);
+                       input_report_abs(dev, ABS_MISC, p);
+                       input_event(dev, EV_MSC, MSC_SERIAL, 0);
+               }
+       }
+       // Report 5s come from the macro keys when pressed by mouse
+       else if (data[0] == 5) {
+               int p = data[1] & 0x01;
+               int dv = data[1] & 0x02;
+               int left = data[1] & 0x04;
+               int right = data[1] & 0x08;
+               int middle = data[1] & 0x10;
+               int macro = data[3];
+
+               if (dv != 0) {
+                       input_report_key(dev, BTN_LEFT, left);
+                       input_report_key(dev, BTN_MIDDLE, middle);
+                       input_report_key(dev, BTN_RIGHT, right);
+
+                       jitterable = left | middle | right;
+
+                       input_report_key(dev, macroKeyEvents[macro - 1], 1);
+                       input_report_rel(dev, ABS_MISC, p);
+                       input_event(dev, EV_MSC, MSC_SERIAL, 0);
+               }
+       }
+       // We have no idea which tool can generate a report 6. Theoretically,
+       // neither need to, having been given reports 4 & 5 for such use.
+       // However, report 6 is the 'official-looking' report for macroKeys;
+       // reports 4 & 5 supposively are used to support unnamed, unknown
+       // hat switches (which just so happen to be the macroKeys.)
+       else if (data[0] == 6) {
+               int macro = ((__u32) data[1]) | ((__u32) data[2] << 8);
+
+               input_report_key(dev, macroKeyEvents[macro - 1], 1);
+               input_report_abs(dev, ABS_MISC, 1);
+               input_event(dev, EV_MSC, MSC_SERIAL, 0);
+       } else {
+               dbg("Unknown report %d", data[0]);
        }
 
+       // Jitter may occur when the user presses a button on the stlyus
+       // 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.
+       if (jitterable != 0 && aiptek->jitterDelay != 0) {
+               wait_ms(aiptek->jitterDelay);
+       }
 }
 
+/*
+ * We are not able to reliably determine the tablet featureset by
+ * asking for the USB productID. Therefore, we will query the
+ * tablet dynamically and populate the struct in aiptek_probe().
+ */
+
 struct aiptek_features aiptek_features[] = {
-       {"Aiptek 6000U/8000U", 
-        8, 3000, 2250, 26, 511, aiptek_irq, 0, 0, 0, 0},
+       {"Aiptek", 8, 0, 0, 0, 0, 0, 0, aiptek_irq},
        {NULL, 0}
 };
 
+/*
+ * These are the USB id's known so far. We do not identify them to
+ * specific Aiptek model numbers, because there has been overlaps,
+ * use, and reuse of id's in existing models. Certain models have
+ * been known to use more than one ID, indicative perhaps of
+ * manufacturing revisions. In any event, we consider these 
+ * IDs to not be model-specific nor unique.
+ */
+
 struct usb_device_id aiptek_ids[] = {
-       {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23), driver_info:0},
+      {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24), driver_info:0},
        {}
 };
 
@@ -179,8 +773,7 @@
 aiptek_open(struct input_dev *dev)
 {
        struct aiptek *aiptek = dev->private;
-
-       if (aiptek->open++)
+       if (aiptek->open_count++)
                return 0;
 
        aiptek->irq->dev = aiptek->usbdev;
@@ -195,31 +788,416 @@
 {
        struct aiptek *aiptek = dev->private;
 
-       if (!--aiptek->open)
+       if (!--aiptek->open_count)
                usb_unlink_urb(aiptek->irq);
 }
 
+/*
+ * Send a command to the tablet. No reply is expected.
+ */
 static void
 aiptek_command(struct usb_device *dev, unsigned int ifnum,
               unsigned char command, unsigned char data)
 {
        __u8 buf[3];
 
-       buf[0] = 4;
+       buf[0] = 2;
+       buf[1] = command;
+       buf[2] = data;
+
+       if (usb_set_report(dev, ifnum, 3, 2, buf, sizeof (buf)) != sizeof (buf)) {
+               dbg("aiptek_command failed, sending: 0x%02x 0x%02x", command,
+                   data);
+       }
+}
+
+/*
+ * Send a query to the tablet. This is done by sending the query stream
+ * first as a command, waiting a few milliseconds, then submitting the
+ * same stream as a query.
+ */
+static unsigned int
+aiptek_query(struct usb_device *dev, unsigned int ifnum,
+            unsigned char command, unsigned char data)
+{
+       unsigned int ret;
+       __u8 buf[8];
+       buf[0] = 2;
        buf[1] = command;
        buf[2] = data;
 
-       if (usb_set_report(dev, ifnum, 3, 2, buf, 3) != 3) {
-               dbg("aiptek_command: 0x%x 0x%x\n", command, data);
+       aiptek_command(dev, ifnum, command, data);
+       wait_ms(400);
+
+       if (usb_get_report(dev, ifnum, 3, 2, buf, 3) < 3) {
+               dbg("aiptek_query failed: returns 0x%02x 0x%02x 0x%02x",
+                   buf[0], buf[1], buf[2]);
+               return 0;
+       }
+       ret = ((__u32) buf[1]) | ((__u32) buf[2] << 8);
+       return ret;
+}
+
+/*
+ * Program the tablet into either absolute or relative mode.
+ *
+ * We also get information about the tablet's size.
+ */
+static void
+aiptek_program_tablet(struct aiptek *aiptek)
+{
+       int modelCode, odmCode, firmwareCode;
+       int xResolution, yResolution, zResolution;
+
+       aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA;
+
+       // execute Resolution500LPI
+       aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x18, 0x04);
+       // query getModelCode
+       modelCode = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x02, 0x00);
+       // query getODMCode
+       odmCode = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x03, 0x00);
+       // query getFirmwareCode
+       firmwareCode = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x04, 0x00);
+       // query getXextension
+       xResolution = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x01, 0x00);
+       // query getYextension
+       yResolution = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x01, 0x01);
+       // query getPressureLevels
+       zResolution = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x08, 0x00);
+
+       // Depending on whether we are in absolute or relative mode, we will
+       // do a switchToTablet(absolute) or switchToMouse(relative) command.
+       if (aiptek->coordinate_mode == AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+               // execute switchToTablet
+               aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x10, 0x01);
+       } else {
+               // execute switchToMouse
+               aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x10, 0x00);
+       }
+       // This command enables the macro keys
+       aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x11, 0x02);
+       // execute FilterOn
+       aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x17, 0x00);
+       // execute AutoGainOn
+       aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x12, 0xff);
+
+       aiptek->features->odmCode = odmCode;
+       aiptek->features->modelCode = modelCode & 0xff;
+       aiptek->features->firmwareCode = firmwareCode;
+       aiptek->features->pressure_max = zResolution;
+       aiptek->features->x_max = xResolution;
+       aiptek->features->y_max = yResolution;
+
+       aiptek->eventCount = 0;
+}
+
+#if defined(CONFIG_PROC_FS)
+/*
+ * This routine determines keywords and their associated values, and
+ * maps them to supported modes in this driver. It's input comes from
+ * aiptek_procfs_write().
+ */
+static void
+aiptek_procfs_parse(struct aiptek *aiptek, char *keyword, char *value)
+{
+       if (strcmp(keyword, "pointer") == 0) {
+               if (strcmp(value, "stylus") == 0) {
+                       aiptek->pointer_mode = AIPTEK_POINTER_ONLY_STYLUS_MODE;
+               } else if (strcmp(value, "mouse") == 0) {
+                       aiptek->pointer_mode = AIPTEK_POINTER_ONLY_MOUSE_MODE;
+               } else if (strcmp(value, "either") == 0) {
+                       aiptek->pointer_mode = AIPTEK_POINTER_EITHER_MODE;
+               }
+       } else if (strcmp(keyword, "coordinate") == 0) {
+               if (strcmp(value, "relative") == 0) {
+                       aiptek->coordinate_mode =
+                           AIPTEK_COORDINATE_RELATIVE_MODE;
+               } else if (strcmp(value, "absolute") == 0) {
+                       aiptek->coordinate_mode =
+                           AIPTEK_COORDINATE_ABSOLUTE_MODE;
+               }
+       } else if (strcmp(keyword, "xtilt") == 0) {
+               if (strcmp(value, "disable") == 0) {
+                       aiptek->xTilt = AIPTEK_TILT_DISABLE;
+               } else {
+                       int x = (int) simple_strtol(value, 0, 10);
+                       if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX)
+                               aiptek->xTilt = x;
+               }
+       } else if (strcmp(keyword, "ytilt") == 0) {
+               if (strcmp(value, "disable") == 0) {
+                       aiptek->yTilt = AIPTEK_TILT_DISABLE;
+               } else {
+                       int y = (int) simple_strtol(value, 0, 10);
+                       if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX)
+                               aiptek->yTilt = y;
+               }
+       } else if (strcmp(keyword, "jitter") == 0) {
+               aiptek->jitterDelay = (int) simple_strtol(value, 0, 10);
+       } else if (strcmp(keyword, "tool") == 0) {
+               if (strcmp(value, "mouse") == 0) {
+                       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_MOUSE_MODE;
+               } else if (strcmp(value, "rubber") == 0) {
+                       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_RUBBER_MODE;
+               } else if (strcmp(value, "pencil") == 0) {
+                       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_PENCIL_MODE;
+               } else if (strcmp(value, "pen") == 0) {
+                       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+               } else if (strcmp(value, "brush") == 0) {
+                       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_BRUSH_MODE;
+               } else if (strcmp(value, "airbrush") == 0) {
+                       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE;
+               }
+       }
+}
+
+/*
+ * This routine reads the status of the aiptek driver, and makes it
+ * available as a procfs file. The description of the procfs file
+ * is at the top of this driver source code.
+ */
+static int
+aiptek_procfs_read(char *page, char **start, off_t offset, int count,
+                  int *eof, void *data)
+{
+       int len;
+       char *out = page;
+       struct aiptek *aiptek = data;
+
+       int inchX = aiptek->features->x_max / 375 * 100;
+       int inchY = aiptek->features->y_max / 375 * 100;
+       int mmX = (int) ((double) aiptek->features->x_max / 14.8);
+       int mmY = (int) ((double) aiptek->features->y_max / 14.8);
+
+       out +=
+           sprintf(out, "Aiptek Tablet (%dx%d, %d.%02dx%d.%02d\", %dx%dmm)\n",
+                   aiptek->features->x_max, aiptek->features->y_max,
+                   inchX / 100, inchX % 100, inchY / 100, inchY % 100, mmX,
+                   mmY);
+
+       out +=
+           sprintf(out,
+                   "(USB VendorID 0x%04x, ProductID 0x%04x, ODMCode 0x%04x\n",
+                   aiptek->dev.idvendor, aiptek->dev.idproduct,
+                   aiptek->features->odmCode);
+       out +=
+           sprintf(out, " ModelCode: 0x%02x, FirmwareCode: 0x%04x)\n",
+                   aiptek->features->modelCode,
+                   aiptek->features->firmwareCode);
+
+       out += sprintf(out, "on /dev/input/event%d\n", aiptek->dev.number);
+       out += sprintf(out, "pointer=%s\n",
+                      (aiptek->pointer_mode == AIPTEK_POINTER_ONLY_MOUSE_MODE
+                       ? "mouse"
+                       : (aiptek->pointer_mode ==
+                          AIPTEK_POINTER_ONLY_STYLUS_MODE ? "stylus" :
+                          "either")));
+       out +=
+           sprintf(out, "coordinate=%s\n",
+                   (aiptek->coordinate_mode ==
+                    AIPTEK_COORDINATE_RELATIVE_MODE ? "relative" :
+                    "absolute"));
+
+       out += sprintf(out, "tool=");
+       switch (aiptek->tool_mode) {
+       case AIPTEK_TOOL_BUTTON_MOUSE_MODE:
+               out += sprintf(out, "mouse\n");
+               break;
+
+       case AIPTEK_TOOL_BUTTON_RUBBER_MODE:
+               out += sprintf(out, "rubber\n");
+               break;
+
+       case AIPTEK_TOOL_BUTTON_PEN_MODE:
+               out += sprintf(out, "pen\n");
+               break;
+
+       case AIPTEK_TOOL_BUTTON_PENCIL_MODE:
+               out += sprintf(out, "pencil\n");
+               break;
+
+       case AIPTEK_TOOL_BUTTON_BRUSH_MODE:
+               out += sprintf(out, "brush\n");
+               break;
+
+       case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE:
+               out += sprintf(out, "airbrush\n");
+               break;
        }
+
+       out += sprintf(out, "xtilt=");
+       if (aiptek->xTilt == AIPTEK_TILT_DISABLE) {
+               out += sprintf(out, "disable\n");
+       } else {
+               out += sprintf(out, "%d\n", aiptek->xTilt);
+       }
+
+       out += sprintf(out, "ytilt=");
+       if (aiptek->yTilt == AIPTEK_TILT_DISABLE) {
+               out += sprintf(out, "disable\n");
+       } else {
+               out += sprintf(out, "%d\n", aiptek->yTilt);
+       }
+
+       out += sprintf(out, "jitter=%d\n", aiptek->jitterDelay);
+
+       out += sprintf(out, "diagnostic=");
+       switch (aiptek->diagnostic) {
+       case AIPTEK_DIAGNOSTIC_NA:
+               out += sprintf(out, "none\n");
+               break;
+       case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE:
+               out += sprintf(out, "tablet sending relative reports\n");
+               break;
+       case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE:
+               out += sprintf(out, "tablet sending absolute reports\n");
+               break;
+       case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED:
+               out += sprintf(out, "tablet seeing reports from ");
+               if (aiptek->pointer_mode == AIPTEK_POINTER_ONLY_MOUSE_MODE)
+                       out += sprintf(out, "stylus\n");
+               else
+                       out += sprintf(out, "mouse\n");
+               break;
+       }
+
+       out += sprintf(out, "eventsReceived=%lu\n", aiptek->eventCount);
+
+       len = out - page;
+       len -= offset;
+       if (len < count) {
+               *eof = 1;
+               if (len <= 0) {
+                       return 0;
+               }
+       } else {
+               len = count;
+       }
+
+       *start = page + offset;
+
+       return len;
 }
 
-static void*
+/*
+ * This routine permits the setting of driver parameters through a
+ * procfs file. Writing to the procfs file (/proc/driver/usb/aiptek),
+ * you can program the tablet's behavior. Parameters that can be programmed
+ * (and their legal values) are described at the top of this driver.
+ *
+ *
+ * This parser is order-insensitive, and supports one or many parameters
+ * to be sent in one write request. As many parameters as you may fit
+ * in 64 bytes; we only require that you separate them with \n's.
+ *
+ * Any command that is not understood by the parser is silently ignored.
+ */
+static int
+aiptek_procfs_write(struct file *file, const char *buffer, unsigned long count,
+                   void *data)
+{
+       char buf[64];
+       char *scan;
+       char *keyword = NULL;
+       char *value = NULL;
+       struct aiptek *aiptek = data;
+       int num;
+
+       num = (count < 64) ? count : 64;
+       copy_from_user(buf, buffer, num);
+       buf[num] = '\0';
+
+       scan = buf;
+       while (*scan) {
+               if (*scan == '\n' || *scan == '\0') {
+                       if (*scan == '\n') {
+                               *scan = '\0';
+                               scan++;
+                       }
+                       if (keyword && value) {
+                               aiptek_procfs_parse(aiptek, keyword, value);
+                       }
+                       keyword = NULL;
+                       value = NULL;
+                       continue;
+               }
+
+               if (*scan != '=' && keyword == NULL) {
+                       keyword = scan;
+               } else if (*scan == '=') {
+                       *scan++ = '\0';
+                       value = scan;
+               }
+               scan++;
+       }
+       // We're insensitive as to whether the buffer ended in a \n or not.
+       if (keyword && value) {
+               aiptek_procfs_parse(aiptek, keyword, value);
+       }
+
+       aiptek_program_tablet(aiptek);
+
+       return num;
+}
+
+/*
+ * This routine destroys our procfs device interface. This will occur
+ * when you remove the driver, either through rmmod or the hotplug system.
+ */
+static void
+destroy_procfs_file(struct aiptek *aiptek)
+{
+       if (aiptek->aiptekProcfsEntry)
+               remove_proc_entry("aiptek", aiptek->usbProcfsEntry);
+       if (aiptek->usbProcfsEntry)
+               remove_proc_entry("usb", proc_root_driver);
+
+       aiptek->usbProcfsEntry = NULL;
+       aiptek->aiptekProcfsEntry = NULL;
+}
+
+/*
+ * This routine builds the procfs file. The file is located at,
+ *      procfs/driver/usb/aiptek.
+ */
+static void
+create_procfs_file(struct aiptek *aiptek)
+{
+       // Make procfs/driver/usb directory
+       aiptek->usbProcfsEntry = create_proc_entry("usb", S_IFDIR,
+                                                  proc_root_driver);
+       if (!aiptek->usbProcfsEntry) {
+               dbg("create_procfs_file failed; no procfs/driver/usb control file.");
+               destroy_procfs_file(aiptek);
+               return;
+       }
+       aiptek->usbProcfsEntry->owner = THIS_MODULE;
+
+       // Make procfs/driver/usb/aiptek file
+       aiptek->aiptekProcfsEntry = create_proc_entry("aiptek",
+                                                     S_IFREG | S_IRUGO |
+                                                     S_IWUGO,
+                                                     aiptek->usbProcfsEntry);
+       if (!aiptek->aiptekProcfsEntry) {
+               dbg("create_procfs_file failed; no procfs/driver/usb control file.");
+               destroy_procfs_file(aiptek);
+               return;
+       }
+       aiptek->aiptekProcfsEntry->owner = THIS_MODULE;
+       aiptek->aiptekProcfsEntry->data = aiptek;
+       aiptek->aiptekProcfsEntry->read_proc = aiptek_procfs_read;
+       aiptek->aiptekProcfsEntry->write_proc = aiptek_procfs_write;
+}
+#endif
+
+static void *
 aiptek_probe(struct usb_device *dev, unsigned int ifnum,
             const struct usb_device_id *id)
 {
        struct usb_endpoint_descriptor *endpoint;
        struct aiptek *aiptek;
+       int i;
 
        if (!(aiptek = kmalloc(sizeof (struct aiptek), GFP_KERNEL)))
                return NULL;
@@ -231,37 +1209,78 @@
                kfree(aiptek);
                return NULL;
        }
+       // This used to be meaningful, when we had a matrix of
+       // different models with statically-assigned different
+       // features. Now we ask the tablet about everything.
+
+       aiptek->features = aiptek_features;
+
+       // Reset the tablet. The tablet boots up in 'SwitchtoMouse'
+       // mode, which indicates relative coordinates. 'SwitchToTablet'
+       // infers absolute coordinates. (Ergo, mice are inferred to be
+       // relative-only devices, which is not true. A misnomer.)
+       // The routine we use, aiptek_program_tablet, has been generalized
+       // enough such that it's callable through the procfs interface.
+       // This is why we use struct aiptek throughout.
+       aiptek->usbdev = dev;
+       aiptek->ifnum = ifnum;
+       aiptek->pointer_mode = AIPTEK_POINTER_EITHER_MODE;
+       aiptek->coordinate_mode = AIPTEK_COORDINATE_ABSOLUTE_MODE;
+       aiptek->tool_mode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+       aiptek->xTilt = AIPTEK_TILT_DISABLE;
+       aiptek->yTilt = AIPTEK_TILT_DISABLE;
+       aiptek->jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
+
+#ifdef CONFIG_PROC_FS
+       create_procfs_file(aiptek);
+#endif
+
+       aiptek_program_tablet(aiptek);
+
+       aiptek->dev.evbit[0] |= BIT(EV_KEY)
+           | BIT(EV_ABS)
+           | BIT(EV_MSC);
+
+       aiptek->dev.absbit[0] |= BIT(ABS_X)
+           | BIT(ABS_Y)
+           | BIT(ABS_PRESSURE)
+           | BIT(ABS_TILT_X)
+           | BIT(ABS_TILT_Y)
+           | BIT(ABS_MISC);
+
+       aiptek->dev.relbit[0] |= BIT(REL_X)
+           | BIT(REL_Y)
+           | BIT(REL_MISC);
+
+       // Set the macro keys up. They are discontiguous, so it's better
+       // to set the bitmask this way.
+
+       for (i = 0; i < sizeof (macroKeyEvents) / sizeof (macroKeyEvents[0]);
+            ++i) {
+               set_bit(macroKeyEvents[i], aiptek->dev.keybit);
+       }
 
-       // Resolution500LPI
-       aiptek_command(dev, ifnum, 0x18, 0x04);
-
-       // SwitchToTablet
-       aiptek_command(dev, ifnum, 0x10, 0x01);
-
-       aiptek->features = aiptek_features + id->driver_info;
-
-       aiptek->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC) |
-           aiptek->features->evbit;
-
-       aiptek->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) |
-           BIT(ABS_MISC) | aiptek->features->absbit;
-
-       aiptek->dev.relbit[0] |= aiptek->features->relbit;
-
-       aiptek->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
-           BIT(BTN_MIDDLE) | aiptek->features->btnbit;
-
-       aiptek->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) |
-           BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOUCH) |
-           BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | aiptek->features->digibit;
+       aiptek->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT)
+           | BIT(BTN_RIGHT)
+           | BIT(BTN_MIDDLE);
+
+       aiptek->dev.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_TOUCH)
+           | BIT(BTN_STYLUS)
+           | BIT(BTN_STYLUS2);
 
        aiptek->dev.mscbit[0] = BIT(MSC_SERIAL);
 
        aiptek->dev.absmax[ABS_X] = aiptek->features->x_max;
        aiptek->dev.absmax[ABS_Y] = aiptek->features->y_max;
-       aiptek->dev.absmax[ABS_PRESSURE] = aiptek->features->pressure_max -
-           aiptek->features->pressure_min;
-
+       aiptek->dev.absmax[ABS_PRESSURE] = aiptek->features->pressure_max;
+       aiptek->dev.absmax[ABS_TILT_X] = AIPTEK_TILT_MAX;
+       aiptek->dev.absmax[ABS_TILT_Y] = AIPTEK_TILT_MAX;
        aiptek->dev.absfuzz[ABS_X] = 0;
        aiptek->dev.absfuzz[ABS_Y] = 0;
 
@@ -278,28 +1297,31 @@
 
        endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
 
-       FILL_INT_URB(aiptek->irq,
-                 dev,
-                 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
-                        aiptek->data,
-                 aiptek->features->pktlen,
-                        aiptek->features->irq,
-                 aiptek,
-                 endpoint->bInterval);
+       usb_fill_int_urb(aiptek->irq,
+                        dev,
+                        usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+                        aiptek->data,
+                        aiptek->features->pktlen,
+                        aiptek->features->irq, aiptek, endpoint->bInterval);
 
        input_register_device(&aiptek->dev);
 
        printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
-              aiptek->dev.number, aiptek->features->name, dev->bus->busnum,
-              dev->devnum, ifnum);
+              aiptek->dev.number,
+              aiptek->features->name, dev->bus->busnum, dev->devnum, ifnum);
 
        return aiptek;
 }
 
+static struct usb_driver aiptek_driver;
+
 static void
 aiptek_disconnect(struct usb_device *dev, void *ptr)
 {
        struct aiptek *aiptek = ptr;
+#ifdef CONFIG_PROC_FS
+       destroy_procfs_file(aiptek);
+#endif
        usb_unlink_urb(aiptek->irq);
        input_unregister_device(&aiptek->dev);
        usb_free_urb(aiptek->irq);
@@ -317,7 +1339,7 @@
 aiptek_init(void)
 {
        usb_register(&aiptek_driver);
-       info(DRIVER_VERSION " " DRIVER_AUTHOR);
+       info(DRIVER_VERSION ": " DRIVER_AUTHOR);
        info(DRIVER_DESC);
        return 0;
 }
diff -Nru a/drivers/usb/hid-core.c b/drivers/usb/hid-core.c
--- a/drivers/usb/hid-core.c    Wed Jun 18 17:34:45 2003
+++ b/drivers/usb/hid-core.c    Wed Jun 18 17:34:45 2003
@@ -1140,6 +1140,15 @@
 #define USB_VENDOR_ID_KBGEAR           0x084e
 #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
 
+#define USB_VENDOR_ID_AIPTEK           0x08ca
+#define USB_DEVICE_ID_AIPTEK_01        0x0001
+#define USB_DEVICE_ID_AIPTEK_10        0x0010
+#define USB_DEVICE_ID_AIPTEK_20        0x0020
+#define USB_DEVICE_ID_AIPTEK_21        0x0021
+#define USB_DEVICE_ID_AIPTEK_22        0x0022
+#define USB_DEVICE_ID_AIPTEK_23        0x0023
+#define USB_DEVICE_ID_AIPTEK_24        0x0024
+
 #define USB_VENDOR_ID_ATEN             0x0557
 #define USB_DEVICE_ID_ATEN_UC100KM     0x2004
 #define USB_DEVICE_ID_ATEN_CS124U      0x2202
@@ -1204,6 +1213,13 @@
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },



-------------------------------------------------------
This SF.Net email is sponsored by: INetU
Attention Web Developers & Consultants: Become An INetU Hosting Partner.
Refer Dedicated Servers. We Manage Them. You Get 10% Monthly Commission!
INetU Dedicated Managed Hosting http://www.inetu.net/partner/index.php
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to