OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [usb/] [aiptek.c] - Rev 1780

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 *  Native support for the Aiptek HyperPen USB Tablets
 *  (4000U/5000U/6000U/8000U/12000U)
 *
 *  Copyright (c) 2001      Chris Atenasio   <chris@crud.net>
 *  Copyright (c) 2002-2003 Bryan W. Headley <bwheadley@earthlink.net>
 *
 *  based on wacom.c by
 *     Vojtech Pavlik      <vojtech@suse.cz>
 *     Andreas Bach Aaen   <abach@stofanet.dk>
 *     Clifford Wolf       <clifford@clifford.at>
 *     Sam Mosel           <sam.mosel@computer.org>
 *     James E. Blair      <corvus@gnu.org>
 *     Daniel Egger        <egger@suse.de>
 *
 *  Many thanks to Oliver Kuechemann for his support.
 *
 *  ChangeLog:
 *      v0.1 - Initial release
 *      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.
 *
 */
 
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
 
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#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 "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);
MODULE_LICENSE("GPL");
 
/*
 * Aiptek status packet:
 * (returned as Report 1)
 *
 *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
 * byte0   0     0     0     0     0     0     1     0
 * 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
 * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
 * byte5   *     *     *    BS2   BS1   Tip   DV    IR
 * byte6  P7    P6    P5    P4    P3    P2    P1    P0
 * byte7  P15   P14   P13   P12   P11   P10   P9    P8
 *
 * 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
 * 0x18/0x04       Resolution500LPI    0
 * 0x17/0x00       FilterOn            0
 * 0x12/0xFF       AutoGainOn          0
 * 0x01/0x00       GetXExtension       2           MaxX
 * 0x01/0x01       GetYExtension       2           MaxY
 * 0x02/0x00       GetModelCode        2           ModelCode = LOBYTE
 * 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 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_max;
	int odmCode;
	int modelCode;
	int firmwareCode;
	void (*irq) (struct urb * urb);
};
 
struct aiptek {
	signed char data[10];
	struct input_dev dev;
	struct usb_device *usbdev;
	struct urb *irq;
	struct aiptek_features *features;
	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;
	int ret;
 
	if (b & 0x80) {
		b = ~b;
		b--;
		negate = 1;
	}
	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 jitterable = 0;
 
	if (urb->status)
		return;
 
	aiptek->eventCount++;
 
	// 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 {
			int x, y, left, right, middle;
 
			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);
			}
			x = aiptek_convert_from_2s_complement(data[2]);
			y = aiptek_convert_from_2s_complement(data[3]);
 
			left = data[5] & 0x01;
			right = data[5] & 0x02;
			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);
 
			input_event(dev, EV_MSC, MSC_SERIAL, 0);
		}
	}
	// 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", 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, 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},
	{}
};
 
MODULE_DEVICE_TABLE(usb, aiptek_ids);
 
static int
aiptek_open(struct input_dev *dev)
{
	struct aiptek *aiptek = dev->private;
	if (aiptek->open_count++)
		return 0;
 
	aiptek->irq->dev = aiptek->usbdev;
	if (usb_submit_urb(aiptek->irq))
		return -EIO;
 
	return 0;
}
 
static void
aiptek_close(struct input_dev *dev)
{
	struct aiptek *aiptek = dev->private;
 
	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] = 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;
 
	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;
 
	out +=
	    sprintf(out, "Aiptek Tablet (%dx%d)\n",
		    aiptek->features->x_max, aiptek->features->y_max);
 
	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;
}
 
/*
 * 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;
	if (copy_from_user(buf, buffer, num))
		return -EFAULT;
	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;
 
	memset(aiptek, 0, sizeof (struct aiptek));
 
	aiptek->irq = usb_alloc_urb(0);
	if (!aiptek->irq) {
		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);
	}
 
	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->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;
 
	aiptek->dev.private = aiptek;
	aiptek->dev.open = aiptek_open;
	aiptek->dev.close = aiptek_close;
 
	aiptek->dev.name = aiptek->features->name;
	aiptek->dev.idbus = BUS_USB;
	aiptek->dev.idvendor = dev->descriptor.idVendor;
	aiptek->dev.idproduct = dev->descriptor.idProduct;
	aiptek->dev.idversion = dev->descriptor.bcdDevice;
	aiptek->usbdev = dev;
 
	endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
 
	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);
 
	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);
	kfree(aiptek);
}
 
static struct usb_driver aiptek_driver = {
	name:"aiptek",
	probe:aiptek_probe,
	disconnect:aiptek_disconnect,
	id_table:aiptek_ids,
};
 
static int __init
aiptek_init(void)
{
	usb_register(&aiptek_driver);
	info(DRIVER_VERSION ": " DRIVER_AUTHOR);
	info(DRIVER_DESC);
	return 0;
}
 
static void __exit
aiptek_exit(void)
{
	usb_deregister(&aiptek_driver);
}
 
module_init(aiptek_init);
module_exit(aiptek_exit);
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.