162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Native support for the Aiptek HyperPen USB Tablets
462306a36Sopenharmony_ci *  (4000U/5000U/6000U/8000U/12000U)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (c) 2001      Chris Atenasio   <chris@crud.net>
762306a36Sopenharmony_ci *  Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  based on wacom.c by
1062306a36Sopenharmony_ci *     Vojtech Pavlik      <vojtech@suse.cz>
1162306a36Sopenharmony_ci *     Andreas Bach Aaen   <abach@stofanet.dk>
1262306a36Sopenharmony_ci *     Clifford Wolf       <clifford@clifford.at>
1362306a36Sopenharmony_ci *     Sam Mosel           <sam.mosel@computer.org>
1462306a36Sopenharmony_ci *     James E. Blair      <corvus@gnu.org>
1562306a36Sopenharmony_ci *     Daniel Egger        <egger@suse.de>
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *  Many thanks to Oliver Kuechemann for his support.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *  ChangeLog:
2062306a36Sopenharmony_ci *      v0.1 - Initial release
2162306a36Sopenharmony_ci *      v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
2262306a36Sopenharmony_ci *      v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
2362306a36Sopenharmony_ci *             Released to Linux 2.4.19 and 2.5.x
2462306a36Sopenharmony_ci *      v0.4 - Rewrote substantial portions of the code to deal with
2562306a36Sopenharmony_ci *             corrected control sequences, timing, dynamic configuration,
2662306a36Sopenharmony_ci *             support of 6000U - 12000U, procfs, and macro key support
2762306a36Sopenharmony_ci *             (Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
2862306a36Sopenharmony_ci *      v1.0 - Added support for diagnostic messages, count of messages
2962306a36Sopenharmony_ci *             received from URB - Mar-8-2003, Bryan W. Headley
3062306a36Sopenharmony_ci *      v1.1 - added support for tablet resolution, changed DV and proximity
3162306a36Sopenharmony_ci *             some corrections - Jun-22-2003, martin schneebacher
3262306a36Sopenharmony_ci *           - Added support for the sysfs interface, deprecating the
3362306a36Sopenharmony_ci *             procfs interface for 2.5.x kernel. Also added support for
3462306a36Sopenharmony_ci *             Wheel command. Bryan W. Headley July-15-2003.
3562306a36Sopenharmony_ci *      v1.2 - Reworked jitter timer as a kernel thread.
3662306a36Sopenharmony_ci *             Bryan W. Headley November-28-2003/Jan-10-2004.
3762306a36Sopenharmony_ci *      v1.3 - Repaired issue of kernel thread going nuts on single-processor
3862306a36Sopenharmony_ci *             machines, introduced programmableDelay as a command line
3962306a36Sopenharmony_ci *             parameter. Feb 7 2004, Bryan W. Headley.
4062306a36Sopenharmony_ci *      v1.4 - Re-wire jitter so it does not require a thread. Courtesy of
4162306a36Sopenharmony_ci *             Rene van Paassen. Added reporting of physical pointer device
4262306a36Sopenharmony_ci *             (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know
4362306a36Sopenharmony_ci *             for reports 1, 6.)
4462306a36Sopenharmony_ci *             what physical device reports for reports 1, 6.) Also enabled
4562306a36Sopenharmony_ci *             MOUSE and LENS tool button modes. Renamed "rubber" to "eraser".
4662306a36Sopenharmony_ci *             Feb 20, 2004, Bryan W. Headley.
4762306a36Sopenharmony_ci *      v1.5 - Added previousJitterable, so we don't do jitter delay when the
4862306a36Sopenharmony_ci *             user is holding a button down for periods of time.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * NOTE:
5162306a36Sopenharmony_ci *      This kernel driver is augmented by the "Aiptek" XFree86 input
5262306a36Sopenharmony_ci *      driver for your X server, as well as the Gaiptek GUI Front-end
5362306a36Sopenharmony_ci *      "Tablet Manager".
5462306a36Sopenharmony_ci *      These three products are highly interactive with one another,
5562306a36Sopenharmony_ci *      so therefore it's easier to document them all as one subsystem.
5662306a36Sopenharmony_ci *      Please visit the project's "home page", located at,
5762306a36Sopenharmony_ci *      http://aiptektablet.sourceforge.net.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#include <linux/jiffies.h>
6162306a36Sopenharmony_ci#include <linux/kernel.h>
6262306a36Sopenharmony_ci#include <linux/slab.h>
6362306a36Sopenharmony_ci#include <linux/module.h>
6462306a36Sopenharmony_ci#include <linux/usb/input.h>
6562306a36Sopenharmony_ci#include <linux/uaccess.h>
6662306a36Sopenharmony_ci#include <asm/unaligned.h>
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * Aiptek status packet:
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * (returned as Report 1 - relative coordinates from mouse and stylus)
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
7462306a36Sopenharmony_ci * byte0   0     0     0     0     0     0     0     1
7562306a36Sopenharmony_ci * byte1   0     0     0     0     0    BS2   BS    Tip
7662306a36Sopenharmony_ci * byte2  X7    X6    X5    X4    X3    X2    X1    X0
7762306a36Sopenharmony_ci * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * (returned as Report 2 - absolute coordinates from the stylus)
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
8262306a36Sopenharmony_ci * byte0   0     0     0     0     0     0     1     0
8362306a36Sopenharmony_ci * byte1  X7    X6    X5    X4    X3    X2    X1    X0
8462306a36Sopenharmony_ci * byte2  X15   X14   X13   X12   X11   X10   X9    X8
8562306a36Sopenharmony_ci * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
8662306a36Sopenharmony_ci * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
8762306a36Sopenharmony_ci * byte5   *     *     *    BS2   BS1   Tip   IR    DV
8862306a36Sopenharmony_ci * byte6  P7    P6    P5    P4    P3    P2    P1    P0
8962306a36Sopenharmony_ci * byte7  P15   P14   P13   P12   P11   P10   P9    P8
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * (returned as Report 3 - absolute coordinates from the mouse)
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
9462306a36Sopenharmony_ci * byte0   0     0     0     0     0     0     1     1
9562306a36Sopenharmony_ci * byte1  X7    X6    X5    X4    X3    X2    X1    X0
9662306a36Sopenharmony_ci * byte2  X15   X14   X13   X12   X11   X10   X9    X8
9762306a36Sopenharmony_ci * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
9862306a36Sopenharmony_ci * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
9962306a36Sopenharmony_ci * byte5   *     *     *    BS2   BS1   Tip   IR    DV
10062306a36Sopenharmony_ci * byte6  P7    P6    P5    P4    P3    P2    P1    P0
10162306a36Sopenharmony_ci * byte7  P15   P14   P13   P12   P11   P10   P9    P8
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * (returned as Report 4 - macrokeys from the stylus)
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
10662306a36Sopenharmony_ci * byte0   0     0     0     0     0     1     0     0
10762306a36Sopenharmony_ci * byte1   0     0     0    BS2   BS    Tip   IR    DV
10862306a36Sopenharmony_ci * byte2   0     0     0     0     0     0     1     0
10962306a36Sopenharmony_ci * byte3   0     0     0    K4    K3    K2    K1    K0
11062306a36Sopenharmony_ci * byte4  P7    P6    P5    P4    P3    P2    P1    P0
11162306a36Sopenharmony_ci * byte5  P15   P14   P13   P12   P11   P10   P9    P8
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * (returned as Report 5 - macrokeys from the mouse)
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
11662306a36Sopenharmony_ci * byte0   0     0     0     0     0     1     0     1
11762306a36Sopenharmony_ci * byte1   0     0     0    BS2   BS    Tip   IR    DV
11862306a36Sopenharmony_ci * byte2   0     0     0     0     0     0     1     0
11962306a36Sopenharmony_ci * byte3   0     0     0    K4    K3    K2    K1    K0
12062306a36Sopenharmony_ci * byte4  P7    P6    P5    P4    P3    P2    P1    P0
12162306a36Sopenharmony_ci * byte5  P15   P14   P13   P12   P11   P10   P9    P8
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * IR: In Range = Proximity on
12462306a36Sopenharmony_ci * DV = Data Valid
12562306a36Sopenharmony_ci * BS = Barrel Switch (as in, macro keys)
12662306a36Sopenharmony_ci * BS2 also referred to as Tablet Pick
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * Command Summary:
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * Use report_type CONTROL (3)
13162306a36Sopenharmony_ci * Use report_id   2
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * Command/Data    Description     Return Bytes    Return Value
13462306a36Sopenharmony_ci * 0x10/0x00       SwitchToMouse       0
13562306a36Sopenharmony_ci * 0x10/0x01       SwitchToTablet      0
13662306a36Sopenharmony_ci * 0x18/0x04       SetResolution       0
13762306a36Sopenharmony_ci * 0x12/0xFF       AutoGainOn          0
13862306a36Sopenharmony_ci * 0x17/0x00       FilterOn            0
13962306a36Sopenharmony_ci * 0x01/0x00       GetXExtension       2           MaxX
14062306a36Sopenharmony_ci * 0x01/0x01       GetYExtension       2           MaxY
14162306a36Sopenharmony_ci * 0x02/0x00       GetModelCode        2           ModelCode = LOBYTE
14262306a36Sopenharmony_ci * 0x03/0x00       GetODMCode          2           ODMCode
14362306a36Sopenharmony_ci * 0x08/0x00       GetPressureLevels   2           =512
14462306a36Sopenharmony_ci * 0x04/0x00       GetFirmwareVersion  2           Firmware Version
14562306a36Sopenharmony_ci * 0x11/0x02       EnableMacroKeys     0
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * To initialize the tablet:
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * (1) Send Resolution500LPI (Command)
15062306a36Sopenharmony_ci * (2) Query for Model code (Option Report)
15162306a36Sopenharmony_ci * (3) Query for ODM code (Option Report)
15262306a36Sopenharmony_ci * (4) Query for firmware (Option Report)
15362306a36Sopenharmony_ci * (5) Query for GetXExtension (Option Report)
15462306a36Sopenharmony_ci * (6) Query for GetYExtension (Option Report)
15562306a36Sopenharmony_ci * (7) Query for GetPressureLevels (Option Report)
15662306a36Sopenharmony_ci * (8) SwitchToTablet for Absolute coordinates, or
15762306a36Sopenharmony_ci *     SwitchToMouse for Relative coordinates (Command)
15862306a36Sopenharmony_ci * (9) EnableMacroKeys (Command)
15962306a36Sopenharmony_ci * (10) FilterOn (Command)
16062306a36Sopenharmony_ci * (11) AutoGainOn (Command)
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * (Step 9 can be omitted, but you'll then have no function keys.)
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define USB_VENDOR_ID_AIPTEK				0x08ca
16662306a36Sopenharmony_ci#define USB_VENDOR_ID_KYE				0x0458
16762306a36Sopenharmony_ci#define USB_REQ_GET_REPORT				0x01
16862306a36Sopenharmony_ci#define USB_REQ_SET_REPORT				0x09
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* PointerMode codes
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci#define AIPTEK_POINTER_ONLY_MOUSE_MODE			0
17362306a36Sopenharmony_ci#define AIPTEK_POINTER_ONLY_STYLUS_MODE			1
17462306a36Sopenharmony_ci#define AIPTEK_POINTER_EITHER_MODE			2
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a)		\
17762306a36Sopenharmony_ci	(a == AIPTEK_POINTER_ONLY_MOUSE_MODE ||		\
17862306a36Sopenharmony_ci	 a == AIPTEK_POINTER_EITHER_MODE)
17962306a36Sopenharmony_ci#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a)		\
18062306a36Sopenharmony_ci	(a == AIPTEK_POINTER_ONLY_STYLUS_MODE ||	\
18162306a36Sopenharmony_ci	 a == AIPTEK_POINTER_EITHER_MODE)
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* CoordinateMode code
18462306a36Sopenharmony_ci	 */
18562306a36Sopenharmony_ci#define AIPTEK_COORDINATE_RELATIVE_MODE			0
18662306a36Sopenharmony_ci#define AIPTEK_COORDINATE_ABSOLUTE_MODE			1
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci       /* XTilt and YTilt values
18962306a36Sopenharmony_ci        */
19062306a36Sopenharmony_ci#define AIPTEK_TILT_MIN					(-128)
19162306a36Sopenharmony_ci#define AIPTEK_TILT_MAX					127
19262306a36Sopenharmony_ci#define AIPTEK_TILT_DISABLE				(-10101)
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Wheel values
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci#define AIPTEK_WHEEL_MIN				0
19762306a36Sopenharmony_ci#define AIPTEK_WHEEL_MAX				1024
19862306a36Sopenharmony_ci#define AIPTEK_WHEEL_DISABLE				(-10101)
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* ToolCode values, which BTW are 0x140 .. 0x14f
20162306a36Sopenharmony_ci	 * We have things set up such that if the tool button has changed,
20262306a36Sopenharmony_ci	 * the tools get reset.
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	/* toolMode codes
20562306a36Sopenharmony_ci	 */
20662306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
20762306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_PENCIL_MODE			BTN_TOOL_PENCIL
20862306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_BRUSH_MODE			BTN_TOOL_BRUSH
20962306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE		BTN_TOOL_AIRBRUSH
21062306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_ERASER_MODE			BTN_TOOL_RUBBER
21162306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_MOUSE_MODE			BTN_TOOL_MOUSE
21262306a36Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_LENS_MODE			BTN_TOOL_LENS
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Diagnostic message codes
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_NA				0
21762306a36Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE	1
21862306a36Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE	2
21962306a36Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED		3
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Time to wait (in ms) to help mask hand jittering
22262306a36Sopenharmony_ci	 * when pressing the stylus buttons.
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci#define AIPTEK_JITTER_DELAY_DEFAULT			50
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Time to wait (in ms) in-between sending the tablet
22762306a36Sopenharmony_ci	 * a command and beginning the process of reading the return
22862306a36Sopenharmony_ci	 * sequence from the tablet.
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_25		25
23162306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_50		50
23262306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_100		100
23362306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_200		200
23462306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_300		300
23562306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_400		400
23662306a36Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT	AIPTEK_PROGRAMMABLE_DELAY_400
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Mouse button programming
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci#define AIPTEK_MOUSE_LEFT_BUTTON		0x04
24162306a36Sopenharmony_ci#define AIPTEK_MOUSE_RIGHT_BUTTON		0x08
24262306a36Sopenharmony_ci#define AIPTEK_MOUSE_MIDDLE_BUTTON		0x10
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Stylus button programming
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci#define AIPTEK_STYLUS_LOWER_BUTTON		0x08
24762306a36Sopenharmony_ci#define AIPTEK_STYLUS_UPPER_BUTTON		0x10
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Length of incoming packet from the tablet
25062306a36Sopenharmony_ci	 */
25162306a36Sopenharmony_ci#define AIPTEK_PACKET_LENGTH			8
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* We report in EV_MISC both the proximity and
25462306a36Sopenharmony_ci	 * whether the report came from the stylus, tablet mouse
25562306a36Sopenharmony_ci	 * or "unknown" -- Unknown when the tablet is in relative
25662306a36Sopenharmony_ci	 * mode, because we only get report 1's.
25762306a36Sopenharmony_ci	 */
25862306a36Sopenharmony_ci#define AIPTEK_REPORT_TOOL_UNKNOWN		0x10
25962306a36Sopenharmony_ci#define AIPTEK_REPORT_TOOL_STYLUS		0x20
26062306a36Sopenharmony_ci#define AIPTEK_REPORT_TOOL_MOUSE		0x40
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
26362306a36Sopenharmony_cistatic int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistruct aiptek_features {
26662306a36Sopenharmony_ci	int odmCode;		/* Tablet manufacturer code       */
26762306a36Sopenharmony_ci	int modelCode;		/* Tablet model code (not unique) */
26862306a36Sopenharmony_ci	int firmwareCode;	/* prom/eeprom version            */
26962306a36Sopenharmony_ci	char usbPath[64 + 1];	/* device's physical usb path     */
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistruct aiptek_settings {
27362306a36Sopenharmony_ci	int pointerMode;	/* stylus-, mouse-only or either */
27462306a36Sopenharmony_ci	int coordinateMode;	/* absolute/relative coords      */
27562306a36Sopenharmony_ci	int toolMode;		/* pen, pencil, brush, etc. tool */
27662306a36Sopenharmony_ci	int xTilt;		/* synthetic xTilt amount        */
27762306a36Sopenharmony_ci	int yTilt;		/* synthetic yTilt amount        */
27862306a36Sopenharmony_ci	int wheel;		/* synthetic wheel amount        */
27962306a36Sopenharmony_ci	int stylusButtonUpper;	/* stylus upper btn delivers...  */
28062306a36Sopenharmony_ci	int stylusButtonLower;	/* stylus lower btn delivers...  */
28162306a36Sopenharmony_ci	int mouseButtonLeft;	/* mouse left btn delivers...    */
28262306a36Sopenharmony_ci	int mouseButtonMiddle;	/* mouse middle btn delivers...  */
28362306a36Sopenharmony_ci	int mouseButtonRight;	/* mouse right btn delivers...   */
28462306a36Sopenharmony_ci	int programmableDelay;	/* delay for tablet programming  */
28562306a36Sopenharmony_ci	int jitterDelay;	/* delay for hand jittering      */
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistruct aiptek {
28962306a36Sopenharmony_ci	struct input_dev *inputdev;		/* input device struct           */
29062306a36Sopenharmony_ci	struct usb_interface *intf;		/* usb interface struct          */
29162306a36Sopenharmony_ci	struct urb *urb;			/* urb for incoming reports      */
29262306a36Sopenharmony_ci	dma_addr_t data_dma;			/* our dma stuffage              */
29362306a36Sopenharmony_ci	struct aiptek_features features;	/* tablet's array of features    */
29462306a36Sopenharmony_ci	struct aiptek_settings curSetting;	/* tablet's current programmable */
29562306a36Sopenharmony_ci	struct aiptek_settings newSetting;	/* ... and new param settings    */
29662306a36Sopenharmony_ci	unsigned int ifnum;			/* interface number for IO       */
29762306a36Sopenharmony_ci	int diagnostic;				/* tablet diagnostic codes       */
29862306a36Sopenharmony_ci	unsigned long eventCount;		/* event count                   */
29962306a36Sopenharmony_ci	int inDelay;				/* jitter: in jitter delay?      */
30062306a36Sopenharmony_ci	unsigned long endDelay;			/* jitter: time when delay ends  */
30162306a36Sopenharmony_ci	int previousJitterable;			/* jitterable prev value     */
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	int lastMacro;				/* macro key to reset            */
30462306a36Sopenharmony_ci	int previousToolMode;			/* pen, pencil, brush, etc. tool */
30562306a36Sopenharmony_ci	unsigned char *data;			/* incoming packet data          */
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic const int eventTypes[] = {
30962306a36Sopenharmony_ci        EV_KEY, EV_ABS, EV_REL, EV_MSC,
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic const int absEvents[] = {
31362306a36Sopenharmony_ci        ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y,
31462306a36Sopenharmony_ci        ABS_WHEEL, ABS_MISC,
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic const int relEvents[] = {
31862306a36Sopenharmony_ci        REL_X, REL_Y, REL_WHEEL,
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic const int buttonEvents[] = {
32262306a36Sopenharmony_ci	BTN_LEFT, BTN_RIGHT, BTN_MIDDLE,
32362306a36Sopenharmony_ci	BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
32462306a36Sopenharmony_ci	BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH,
32562306a36Sopenharmony_ci	BTN_STYLUS, BTN_STYLUS2,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/*
32962306a36Sopenharmony_ci * Permit easy lookup of keyboard events to send, versus
33062306a36Sopenharmony_ci * the bitmap which comes from the tablet. This hides the
33162306a36Sopenharmony_ci * issue that the F_keys are not sequentially numbered.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cistatic const int macroKeyEvents[] = {
33462306a36Sopenharmony_ci	KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
33562306a36Sopenharmony_ci	KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
33662306a36Sopenharmony_ci	KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
33762306a36Sopenharmony_ci	KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
33862306a36Sopenharmony_ci	KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO,
33962306a36Sopenharmony_ci	KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/***********************************************************************
34362306a36Sopenharmony_ci * Map values to strings and back. Every map should have the following
34462306a36Sopenharmony_ci * as its last element: { NULL, AIPTEK_INVALID_VALUE }.
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_ci#define AIPTEK_INVALID_VALUE	-1
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistruct aiptek_map {
34962306a36Sopenharmony_ci	const char *string;
35062306a36Sopenharmony_ci	int value;
35162306a36Sopenharmony_ci};
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	const struct aiptek_map *p;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (str[count - 1] == '\n')
35862306a36Sopenharmony_ci		count--;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	for (p = map; p->string; p++)
36162306a36Sopenharmony_ci	        if (!strncmp(str, p->string, count))
36262306a36Sopenharmony_ci			return p->value;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return AIPTEK_INVALID_VALUE;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic const char *map_val_to_str(const struct aiptek_map *map, int val)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	const struct aiptek_map *p;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	for (p = map; p->value != AIPTEK_INVALID_VALUE; p++)
37262306a36Sopenharmony_ci		if (val == p->value)
37362306a36Sopenharmony_ci			return p->string;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return "unknown";
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/***********************************************************************
37962306a36Sopenharmony_ci * aiptek_irq can receive one of six potential reports.
38062306a36Sopenharmony_ci * The documentation for each is in the body of the function.
38162306a36Sopenharmony_ci *
38262306a36Sopenharmony_ci * The tablet reports on several attributes per invocation of
38362306a36Sopenharmony_ci * aiptek_irq. Because the Linux Input Event system allows the
38462306a36Sopenharmony_ci * transmission of ONE attribute per input_report_xxx() call,
38562306a36Sopenharmony_ci * collation has to be done on the other end to reconstitute
38662306a36Sopenharmony_ci * a complete tablet report. Further, the number of Input Event reports
38762306a36Sopenharmony_ci * submitted varies, depending on what USB report type, and circumstance.
38862306a36Sopenharmony_ci * To deal with this, EV_MSC is used to indicate an 'end-of-report'
38962306a36Sopenharmony_ci * message. This has been an undocumented convention understood by the kernel
39062306a36Sopenharmony_ci * tablet driver and clients such as gpm and XFree86's tablet drivers.
39162306a36Sopenharmony_ci *
39262306a36Sopenharmony_ci * Of the information received from the tablet, the one piece I
39362306a36Sopenharmony_ci * cannot transmit is the proximity bit (without resorting to an EV_MSC
39462306a36Sopenharmony_ci * convention above.) I therefore have taken over REL_MISC and ABS_MISC
39562306a36Sopenharmony_ci * (for relative and absolute reports, respectively) for communicating
39662306a36Sopenharmony_ci * Proximity. Why two events? I thought it interesting to know if the
39762306a36Sopenharmony_ci * Proximity event occurred while the tablet was in absolute or relative
39862306a36Sopenharmony_ci * mode.
39962306a36Sopenharmony_ci * Update: REL_MISC proved not to be such a good idea. With REL_MISC you
40062306a36Sopenharmony_ci * get an event transmitted each time. ABS_MISC works better, since it
40162306a36Sopenharmony_ci * can be set and re-set. Thus, only using ABS_MISC from now on.
40262306a36Sopenharmony_ci *
40362306a36Sopenharmony_ci * Other tablets use the notion of a certain minimum stylus pressure
40462306a36Sopenharmony_ci * to infer proximity. While that could have been done, that is yet
40562306a36Sopenharmony_ci * another 'by convention' behavior, the documentation for which
40662306a36Sopenharmony_ci * would be spread between two (or more) pieces of software.
40762306a36Sopenharmony_ci *
40862306a36Sopenharmony_ci * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and
40962306a36Sopenharmony_ci * replaced with the input_sync() method (which emits EV_SYN.)
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void aiptek_irq(struct urb *urb)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct aiptek *aiptek = urb->context;
41562306a36Sopenharmony_ci	unsigned char *data = aiptek->data;
41662306a36Sopenharmony_ci	struct input_dev *inputdev = aiptek->inputdev;
41762306a36Sopenharmony_ci	struct usb_interface *intf = aiptek->intf;
41862306a36Sopenharmony_ci	int jitterable = 0;
41962306a36Sopenharmony_ci	int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	switch (urb->status) {
42262306a36Sopenharmony_ci	case 0:
42362306a36Sopenharmony_ci		/* Success */
42462306a36Sopenharmony_ci		break;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	case -ECONNRESET:
42762306a36Sopenharmony_ci	case -ENOENT:
42862306a36Sopenharmony_ci	case -ESHUTDOWN:
42962306a36Sopenharmony_ci		/* This urb is terminated, clean up */
43062306a36Sopenharmony_ci		dev_dbg(&intf->dev, "%s - urb shutting down with status: %d\n",
43162306a36Sopenharmony_ci			__func__, urb->status);
43262306a36Sopenharmony_ci		return;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	default:
43562306a36Sopenharmony_ci		dev_dbg(&intf->dev, "%s - nonzero urb status received: %d\n",
43662306a36Sopenharmony_ci			__func__, urb->status);
43762306a36Sopenharmony_ci		goto exit;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* See if we are in a delay loop -- throw out report if true.
44162306a36Sopenharmony_ci	 */
44262306a36Sopenharmony_ci	if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) {
44362306a36Sopenharmony_ci		goto exit;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	aiptek->inDelay = 0;
44762306a36Sopenharmony_ci	aiptek->eventCount++;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* Report 1 delivers relative coordinates with either a stylus
45062306a36Sopenharmony_ci	 * or the mouse. You do not know, however, which input
45162306a36Sopenharmony_ci	 * tool generated the event.
45262306a36Sopenharmony_ci	 */
45362306a36Sopenharmony_ci	if (data[0] == 1) {
45462306a36Sopenharmony_ci		if (aiptek->curSetting.coordinateMode ==
45562306a36Sopenharmony_ci		    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
45662306a36Sopenharmony_ci			aiptek->diagnostic =
45762306a36Sopenharmony_ci			    AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE;
45862306a36Sopenharmony_ci		} else {
45962306a36Sopenharmony_ci			x = (signed char) data[2];
46062306a36Sopenharmony_ci			y = (signed char) data[3];
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci			/* jitterable keeps track of whether any button has been pressed.
46362306a36Sopenharmony_ci			 * We're also using it to remap the physical mouse button mask
46462306a36Sopenharmony_ci			 * to pseudo-settings. (We don't specifically care about it's
46562306a36Sopenharmony_ci			 * value after moving/transposing mouse button bitmasks, except
46662306a36Sopenharmony_ci			 * that a non-zero value indicates that one or more
46762306a36Sopenharmony_ci			 * mouse button was pressed.)
46862306a36Sopenharmony_ci			 */
46962306a36Sopenharmony_ci			jitterable = data[1] & 0x07;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci			left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0;
47262306a36Sopenharmony_ci			right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0;
47362306a36Sopenharmony_ci			middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci			input_report_key(inputdev, BTN_LEFT, left);
47662306a36Sopenharmony_ci			input_report_key(inputdev, BTN_MIDDLE, middle);
47762306a36Sopenharmony_ci			input_report_key(inputdev, BTN_RIGHT, right);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci			input_report_abs(inputdev, ABS_MISC,
48062306a36Sopenharmony_ci					 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
48162306a36Sopenharmony_ci			input_report_rel(inputdev, REL_X, x);
48262306a36Sopenharmony_ci			input_report_rel(inputdev, REL_Y, y);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci			/* Wheel support is in the form of a single-event
48562306a36Sopenharmony_ci			 * firing.
48662306a36Sopenharmony_ci			 */
48762306a36Sopenharmony_ci			if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
48862306a36Sopenharmony_ci				input_report_rel(inputdev, REL_WHEEL,
48962306a36Sopenharmony_ci						 aiptek->curSetting.wheel);
49062306a36Sopenharmony_ci				aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
49162306a36Sopenharmony_ci			}
49262306a36Sopenharmony_ci			if (aiptek->lastMacro != -1) {
49362306a36Sopenharmony_ci			        input_report_key(inputdev,
49462306a36Sopenharmony_ci						 macroKeyEvents[aiptek->lastMacro], 0);
49562306a36Sopenharmony_ci				aiptek->lastMacro = -1;
49662306a36Sopenharmony_ci			}
49762306a36Sopenharmony_ci			input_sync(inputdev);
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	/* Report 2 is delivered only by the stylus, and delivers
50162306a36Sopenharmony_ci	 * absolute coordinates.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	else if (data[0] == 2) {
50462306a36Sopenharmony_ci		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
50562306a36Sopenharmony_ci			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
50662306a36Sopenharmony_ci		} else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE
50762306a36Sopenharmony_ci			    (aiptek->curSetting.pointerMode)) {
50862306a36Sopenharmony_ci				aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
50962306a36Sopenharmony_ci		} else {
51062306a36Sopenharmony_ci			x = get_unaligned_le16(data + 1);
51162306a36Sopenharmony_ci			y = get_unaligned_le16(data + 3);
51262306a36Sopenharmony_ci			z = get_unaligned_le16(data + 6);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci			dv = (data[5] & 0x01) != 0 ? 1 : 0;
51562306a36Sopenharmony_ci			p = (data[5] & 0x02) != 0 ? 1 : 0;
51662306a36Sopenharmony_ci			tip = (data[5] & 0x04) != 0 ? 1 : 0;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci			/* Use jitterable to re-arrange button masks
51962306a36Sopenharmony_ci			 */
52062306a36Sopenharmony_ci			jitterable = data[5] & 0x18;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci			bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
52362306a36Sopenharmony_ci			pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci			/* dv indicates 'data valid' (e.g., the tablet is in sync
52662306a36Sopenharmony_ci			 * and has delivered a "correct" report) We will ignore
52762306a36Sopenharmony_ci			 * all 'bad' reports...
52862306a36Sopenharmony_ci			 */
52962306a36Sopenharmony_ci			if (dv != 0) {
53062306a36Sopenharmony_ci				/* If the selected tool changed, reset the old
53162306a36Sopenharmony_ci				 * tool key, and set the new one.
53262306a36Sopenharmony_ci				 */
53362306a36Sopenharmony_ci				if (aiptek->previousToolMode !=
53462306a36Sopenharmony_ci				    aiptek->curSetting.toolMode) {
53562306a36Sopenharmony_ci				        input_report_key(inputdev,
53662306a36Sopenharmony_ci							 aiptek->previousToolMode, 0);
53762306a36Sopenharmony_ci					input_report_key(inputdev,
53862306a36Sopenharmony_ci							 aiptek->curSetting.toolMode,
53962306a36Sopenharmony_ci							 1);
54062306a36Sopenharmony_ci					aiptek->previousToolMode =
54162306a36Sopenharmony_ci					          aiptek->curSetting.toolMode;
54262306a36Sopenharmony_ci				}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci				if (p != 0) {
54562306a36Sopenharmony_ci					input_report_abs(inputdev, ABS_X, x);
54662306a36Sopenharmony_ci					input_report_abs(inputdev, ABS_Y, y);
54762306a36Sopenharmony_ci					input_report_abs(inputdev, ABS_PRESSURE, z);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci					input_report_key(inputdev, BTN_TOUCH, tip);
55062306a36Sopenharmony_ci					input_report_key(inputdev, BTN_STYLUS, bs);
55162306a36Sopenharmony_ci					input_report_key(inputdev, BTN_STYLUS2, pck);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci					if (aiptek->curSetting.xTilt !=
55462306a36Sopenharmony_ci					    AIPTEK_TILT_DISABLE) {
55562306a36Sopenharmony_ci						input_report_abs(inputdev,
55662306a36Sopenharmony_ci								 ABS_TILT_X,
55762306a36Sopenharmony_ci								 aiptek->curSetting.xTilt);
55862306a36Sopenharmony_ci					}
55962306a36Sopenharmony_ci					if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) {
56062306a36Sopenharmony_ci						input_report_abs(inputdev,
56162306a36Sopenharmony_ci								 ABS_TILT_Y,
56262306a36Sopenharmony_ci								 aiptek->curSetting.yTilt);
56362306a36Sopenharmony_ci					}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci					/* Wheel support is in the form of a single-event
56662306a36Sopenharmony_ci					 * firing.
56762306a36Sopenharmony_ci					 */
56862306a36Sopenharmony_ci					if (aiptek->curSetting.wheel !=
56962306a36Sopenharmony_ci					    AIPTEK_WHEEL_DISABLE) {
57062306a36Sopenharmony_ci						input_report_abs(inputdev,
57162306a36Sopenharmony_ci								 ABS_WHEEL,
57262306a36Sopenharmony_ci								 aiptek->curSetting.wheel);
57362306a36Sopenharmony_ci						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
57462306a36Sopenharmony_ci					}
57562306a36Sopenharmony_ci				}
57662306a36Sopenharmony_ci				input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS);
57762306a36Sopenharmony_ci				if (aiptek->lastMacro != -1) {
57862306a36Sopenharmony_ci			                input_report_key(inputdev,
57962306a36Sopenharmony_ci							 macroKeyEvents[aiptek->lastMacro], 0);
58062306a36Sopenharmony_ci					aiptek->lastMacro = -1;
58162306a36Sopenharmony_ci				}
58262306a36Sopenharmony_ci				input_sync(inputdev);
58362306a36Sopenharmony_ci			}
58462306a36Sopenharmony_ci		}
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci	/* Report 3's come from the mouse in absolute mode.
58762306a36Sopenharmony_ci	 */
58862306a36Sopenharmony_ci	else if (data[0] == 3) {
58962306a36Sopenharmony_ci		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
59062306a36Sopenharmony_ci			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
59162306a36Sopenharmony_ci		} else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE
59262306a36Sopenharmony_ci			(aiptek->curSetting.pointerMode)) {
59362306a36Sopenharmony_ci			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
59462306a36Sopenharmony_ci		} else {
59562306a36Sopenharmony_ci			x = get_unaligned_le16(data + 1);
59662306a36Sopenharmony_ci			y = get_unaligned_le16(data + 3);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci			jitterable = data[5] & 0x1c;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci			dv = (data[5] & 0x01) != 0 ? 1 : 0;
60162306a36Sopenharmony_ci			p = (data[5] & 0x02) != 0 ? 1 : 0;
60262306a36Sopenharmony_ci			left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
60362306a36Sopenharmony_ci			right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
60462306a36Sopenharmony_ci			middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci			if (dv != 0) {
60762306a36Sopenharmony_ci				/* If the selected tool changed, reset the old
60862306a36Sopenharmony_ci				 * tool key, and set the new one.
60962306a36Sopenharmony_ci				 */
61062306a36Sopenharmony_ci				if (aiptek->previousToolMode !=
61162306a36Sopenharmony_ci				    aiptek->curSetting.toolMode) {
61262306a36Sopenharmony_ci				        input_report_key(inputdev,
61362306a36Sopenharmony_ci							 aiptek->previousToolMode, 0);
61462306a36Sopenharmony_ci					input_report_key(inputdev,
61562306a36Sopenharmony_ci							 aiptek->curSetting.toolMode,
61662306a36Sopenharmony_ci							 1);
61762306a36Sopenharmony_ci					aiptek->previousToolMode =
61862306a36Sopenharmony_ci					          aiptek->curSetting.toolMode;
61962306a36Sopenharmony_ci				}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci				if (p != 0) {
62262306a36Sopenharmony_ci					input_report_abs(inputdev, ABS_X, x);
62362306a36Sopenharmony_ci					input_report_abs(inputdev, ABS_Y, y);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci					input_report_key(inputdev, BTN_LEFT, left);
62662306a36Sopenharmony_ci					input_report_key(inputdev, BTN_MIDDLE, middle);
62762306a36Sopenharmony_ci					input_report_key(inputdev, BTN_RIGHT, right);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci					/* Wheel support is in the form of a single-event
63062306a36Sopenharmony_ci					 * firing.
63162306a36Sopenharmony_ci					 */
63262306a36Sopenharmony_ci					if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
63362306a36Sopenharmony_ci						input_report_abs(inputdev,
63462306a36Sopenharmony_ci								 ABS_WHEEL,
63562306a36Sopenharmony_ci								 aiptek->curSetting.wheel);
63662306a36Sopenharmony_ci						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
63762306a36Sopenharmony_ci					}
63862306a36Sopenharmony_ci				}
63962306a36Sopenharmony_ci				input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
64062306a36Sopenharmony_ci				if (aiptek->lastMacro != -1) {
64162306a36Sopenharmony_ci			                input_report_key(inputdev,
64262306a36Sopenharmony_ci							 macroKeyEvents[aiptek->lastMacro], 0);
64362306a36Sopenharmony_ci				        aiptek->lastMacro = -1;
64462306a36Sopenharmony_ci				}
64562306a36Sopenharmony_ci				input_sync(inputdev);
64662306a36Sopenharmony_ci			}
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	/* Report 4s come from the macro keys when pressed by stylus
65062306a36Sopenharmony_ci	 */
65162306a36Sopenharmony_ci	else if (data[0] == 4) {
65262306a36Sopenharmony_ci		jitterable = data[1] & 0x18;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		dv = (data[1] & 0x01) != 0 ? 1 : 0;
65562306a36Sopenharmony_ci		p = (data[1] & 0x02) != 0 ? 1 : 0;
65662306a36Sopenharmony_ci		tip = (data[1] & 0x04) != 0 ? 1 : 0;
65762306a36Sopenharmony_ci		bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
65862306a36Sopenharmony_ci		pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1;
66162306a36Sopenharmony_ci		z = get_unaligned_le16(data + 4);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (dv) {
66462306a36Sopenharmony_ci		        /* If the selected tool changed, reset the old
66562306a36Sopenharmony_ci			 * tool key, and set the new one.
66662306a36Sopenharmony_ci			 */
66762306a36Sopenharmony_ci		        if (aiptek->previousToolMode !=
66862306a36Sopenharmony_ci			    aiptek->curSetting.toolMode) {
66962306a36Sopenharmony_ci			        input_report_key(inputdev,
67062306a36Sopenharmony_ci						 aiptek->previousToolMode, 0);
67162306a36Sopenharmony_ci				input_report_key(inputdev,
67262306a36Sopenharmony_ci						 aiptek->curSetting.toolMode,
67362306a36Sopenharmony_ci						 1);
67462306a36Sopenharmony_ci				aiptek->previousToolMode =
67562306a36Sopenharmony_ci				        aiptek->curSetting.toolMode;
67662306a36Sopenharmony_ci			}
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) {
68062306a36Sopenharmony_ci		        input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0);
68162306a36Sopenharmony_ci			aiptek->lastMacro = -1;
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		if (macro != -1 && macro != aiptek->lastMacro) {
68562306a36Sopenharmony_ci			input_report_key(inputdev, macroKeyEvents[macro], 1);
68662306a36Sopenharmony_ci			aiptek->lastMacro = macro;
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci		input_report_abs(inputdev, ABS_MISC,
68962306a36Sopenharmony_ci				 p | AIPTEK_REPORT_TOOL_STYLUS);
69062306a36Sopenharmony_ci		input_sync(inputdev);
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	/* Report 5s come from the macro keys when pressed by mouse
69362306a36Sopenharmony_ci	 */
69462306a36Sopenharmony_ci	else if (data[0] == 5) {
69562306a36Sopenharmony_ci		jitterable = data[1] & 0x1c;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		dv = (data[1] & 0x01) != 0 ? 1 : 0;
69862306a36Sopenharmony_ci		p = (data[1] & 0x02) != 0 ? 1 : 0;
69962306a36Sopenharmony_ci		left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
70062306a36Sopenharmony_ci		right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
70162306a36Sopenharmony_ci		middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
70262306a36Sopenharmony_ci		macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		if (dv) {
70562306a36Sopenharmony_ci		        /* If the selected tool changed, reset the old
70662306a36Sopenharmony_ci			 * tool key, and set the new one.
70762306a36Sopenharmony_ci			 */
70862306a36Sopenharmony_ci		        if (aiptek->previousToolMode !=
70962306a36Sopenharmony_ci			    aiptek->curSetting.toolMode) {
71062306a36Sopenharmony_ci		                input_report_key(inputdev,
71162306a36Sopenharmony_ci						 aiptek->previousToolMode, 0);
71262306a36Sopenharmony_ci			        input_report_key(inputdev,
71362306a36Sopenharmony_ci						 aiptek->curSetting.toolMode, 1);
71462306a36Sopenharmony_ci			        aiptek->previousToolMode = aiptek->curSetting.toolMode;
71562306a36Sopenharmony_ci			}
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) {
71962306a36Sopenharmony_ci		        input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0);
72062306a36Sopenharmony_ci			aiptek->lastMacro = -1;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if (macro != -1 && macro != aiptek->lastMacro) {
72462306a36Sopenharmony_ci			input_report_key(inputdev, macroKeyEvents[macro], 1);
72562306a36Sopenharmony_ci			aiptek->lastMacro = macro;
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		input_report_abs(inputdev, ABS_MISC,
72962306a36Sopenharmony_ci				 p | AIPTEK_REPORT_TOOL_MOUSE);
73062306a36Sopenharmony_ci		input_sync(inputdev);
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	/* We have no idea which tool can generate a report 6. Theoretically,
73362306a36Sopenharmony_ci	 * neither need to, having been given reports 4 & 5 for such use.
73462306a36Sopenharmony_ci	 * However, report 6 is the 'official-looking' report for macroKeys;
73562306a36Sopenharmony_ci	 * reports 4 & 5 supposively are used to support unnamed, unknown
73662306a36Sopenharmony_ci	 * hat switches (which just so happen to be the macroKeys.)
73762306a36Sopenharmony_ci	 */
73862306a36Sopenharmony_ci	else if (data[0] == 6) {
73962306a36Sopenharmony_ci		macro = get_unaligned_le16(data + 1);
74062306a36Sopenharmony_ci		if (macro > 0) {
74162306a36Sopenharmony_ci			input_report_key(inputdev, macroKeyEvents[macro - 1],
74262306a36Sopenharmony_ci					 0);
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci		if (macro < 25) {
74562306a36Sopenharmony_ci			input_report_key(inputdev, macroKeyEvents[macro + 1],
74662306a36Sopenharmony_ci					 0);
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		/* If the selected tool changed, reset the old
75062306a36Sopenharmony_ci		   tool key, and set the new one.
75162306a36Sopenharmony_ci		*/
75262306a36Sopenharmony_ci		if (aiptek->previousToolMode !=
75362306a36Sopenharmony_ci		    aiptek->curSetting.toolMode) {
75462306a36Sopenharmony_ci		        input_report_key(inputdev,
75562306a36Sopenharmony_ci					 aiptek->previousToolMode, 0);
75662306a36Sopenharmony_ci			input_report_key(inputdev,
75762306a36Sopenharmony_ci					 aiptek->curSetting.toolMode,
75862306a36Sopenharmony_ci					 1);
75962306a36Sopenharmony_ci			aiptek->previousToolMode =
76062306a36Sopenharmony_ci				aiptek->curSetting.toolMode;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		input_report_key(inputdev, macroKeyEvents[macro], 1);
76462306a36Sopenharmony_ci		input_report_abs(inputdev, ABS_MISC,
76562306a36Sopenharmony_ci				 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
76662306a36Sopenharmony_ci		input_sync(inputdev);
76762306a36Sopenharmony_ci	} else {
76862306a36Sopenharmony_ci		dev_dbg(&intf->dev, "Unknown report %d\n", data[0]);
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* Jitter may occur when the user presses a button on the stlyus
77262306a36Sopenharmony_ci	 * or the mouse. What we do to prevent that is wait 'x' milliseconds
77362306a36Sopenharmony_ci	 * following a 'jitterable' event, which should give the hand some time
77462306a36Sopenharmony_ci	 * stabilize itself.
77562306a36Sopenharmony_ci	 *
77662306a36Sopenharmony_ci	 * We just introduced aiptek->previousJitterable to carry forth the
77762306a36Sopenharmony_ci	 * notion that jitter occurs when the button state changes from on to off:
77862306a36Sopenharmony_ci	 * a person drawing, holding a button down is not subject to jittering.
77962306a36Sopenharmony_ci	 * With that in mind, changing from upper button depressed to lower button
78062306a36Sopenharmony_ci	 * WILL transition through a jitter delay.
78162306a36Sopenharmony_ci	 */
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (aiptek->previousJitterable != jitterable &&
78462306a36Sopenharmony_ci	    aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) {
78562306a36Sopenharmony_ci		aiptek->endDelay = jiffies +
78662306a36Sopenharmony_ci		    ((aiptek->curSetting.jitterDelay * HZ) / 1000);
78762306a36Sopenharmony_ci		aiptek->inDelay = 1;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	aiptek->previousJitterable = jitterable;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ciexit:
79262306a36Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
79362306a36Sopenharmony_ci	if (retval != 0) {
79462306a36Sopenharmony_ci		dev_err(&intf->dev,
79562306a36Sopenharmony_ci			"%s - usb_submit_urb failed with result %d\n",
79662306a36Sopenharmony_ci			__func__, retval);
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/***********************************************************************
80162306a36Sopenharmony_ci * These are the USB id's known so far. We do not identify them to
80262306a36Sopenharmony_ci * specific Aiptek model numbers, because there has been overlaps,
80362306a36Sopenharmony_ci * use, and reuse of id's in existing models. Certain models have
80462306a36Sopenharmony_ci * been known to use more than one ID, indicative perhaps of
80562306a36Sopenharmony_ci * manufacturing revisions. In any event, we consider these
80662306a36Sopenharmony_ci * IDs to not be model-specific nor unique.
80762306a36Sopenharmony_ci */
80862306a36Sopenharmony_cistatic const struct usb_device_id aiptek_ids[] = {
80962306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)},
81062306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)},
81162306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)},
81262306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)},
81362306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)},
81462306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)},
81562306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)},
81662306a36Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)},
81762306a36Sopenharmony_ci	{}
81862306a36Sopenharmony_ci};
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, aiptek_ids);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/***********************************************************************
82362306a36Sopenharmony_ci * Open an instance of the tablet driver.
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_cistatic int aiptek_open(struct input_dev *inputdev)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct aiptek *aiptek = input_get_drvdata(inputdev);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	aiptek->urb->dev = interface_to_usbdev(aiptek->intf);
83062306a36Sopenharmony_ci	if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0)
83162306a36Sopenharmony_ci		return -EIO;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return 0;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci/***********************************************************************
83762306a36Sopenharmony_ci * Close an instance of the tablet driver.
83862306a36Sopenharmony_ci */
83962306a36Sopenharmony_cistatic void aiptek_close(struct input_dev *inputdev)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct aiptek *aiptek = input_get_drvdata(inputdev);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	usb_kill_urb(aiptek->urb);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci/***********************************************************************
84762306a36Sopenharmony_ci * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x,
84862306a36Sopenharmony_ci * where they were known as usb_set_report and usb_get_report.
84962306a36Sopenharmony_ci */
85062306a36Sopenharmony_cistatic int
85162306a36Sopenharmony_ciaiptek_set_report(struct aiptek *aiptek,
85262306a36Sopenharmony_ci		  unsigned char report_type,
85362306a36Sopenharmony_ci		  unsigned char report_id, void *buffer, int size)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(aiptek->intf);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	return usb_control_msg(udev,
85862306a36Sopenharmony_ci			       usb_sndctrlpipe(udev, 0),
85962306a36Sopenharmony_ci			       USB_REQ_SET_REPORT,
86062306a36Sopenharmony_ci			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
86162306a36Sopenharmony_ci			       USB_DIR_OUT, (report_type << 8) + report_id,
86262306a36Sopenharmony_ci			       aiptek->ifnum, buffer, size, 5000);
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic int
86662306a36Sopenharmony_ciaiptek_get_report(struct aiptek *aiptek,
86762306a36Sopenharmony_ci		  unsigned char report_type,
86862306a36Sopenharmony_ci		  unsigned char report_id, void *buffer, int size)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(aiptek->intf);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	return usb_control_msg(udev,
87362306a36Sopenharmony_ci			       usb_rcvctrlpipe(udev, 0),
87462306a36Sopenharmony_ci			       USB_REQ_GET_REPORT,
87562306a36Sopenharmony_ci			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
87662306a36Sopenharmony_ci			       USB_DIR_IN, (report_type << 8) + report_id,
87762306a36Sopenharmony_ci			       aiptek->ifnum, buffer, size, 5000);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci/***********************************************************************
88162306a36Sopenharmony_ci * Send a command to the tablet.
88262306a36Sopenharmony_ci */
88362306a36Sopenharmony_cistatic int
88462306a36Sopenharmony_ciaiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	const int sizeof_buf = 3 * sizeof(u8);
88762306a36Sopenharmony_ci	int ret;
88862306a36Sopenharmony_ci	u8 *buf;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	buf = kmalloc(sizeof_buf, GFP_KERNEL);
89162306a36Sopenharmony_ci	if (!buf)
89262306a36Sopenharmony_ci		return -ENOMEM;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	buf[0] = 2;
89562306a36Sopenharmony_ci	buf[1] = command;
89662306a36Sopenharmony_ci	buf[2] = data;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if ((ret =
89962306a36Sopenharmony_ci	     aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
90062306a36Sopenharmony_ci		dev_dbg(&aiptek->intf->dev,
90162306a36Sopenharmony_ci			"aiptek_program: failed, tried to send: 0x%02x 0x%02x\n",
90262306a36Sopenharmony_ci			command, data);
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	kfree(buf);
90562306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/***********************************************************************
90962306a36Sopenharmony_ci * Retrieve information from the tablet. Querying info is defined as first
91062306a36Sopenharmony_ci * sending the {command,data} sequence as a command, followed by a wait
91162306a36Sopenharmony_ci * (aka, "programmaticDelay") and then a "read" request.
91262306a36Sopenharmony_ci */
91362306a36Sopenharmony_cistatic int
91462306a36Sopenharmony_ciaiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	const int sizeof_buf = 3 * sizeof(u8);
91762306a36Sopenharmony_ci	int ret;
91862306a36Sopenharmony_ci	u8 *buf;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	buf = kmalloc(sizeof_buf, GFP_KERNEL);
92162306a36Sopenharmony_ci	if (!buf)
92262306a36Sopenharmony_ci		return -ENOMEM;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	buf[0] = 2;
92562306a36Sopenharmony_ci	buf[1] = command;
92662306a36Sopenharmony_ci	buf[2] = data;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (aiptek_command(aiptek, command, data) != 0) {
92962306a36Sopenharmony_ci		kfree(buf);
93062306a36Sopenharmony_ci		return -EIO;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci	msleep(aiptek->curSetting.programmableDelay);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf) != sizeof_buf) {
93562306a36Sopenharmony_ci		dev_dbg(&aiptek->intf->dev,
93662306a36Sopenharmony_ci			"aiptek_query failed: returned 0x%02x 0x%02x 0x%02x\n",
93762306a36Sopenharmony_ci			buf[0], buf[1], buf[2]);
93862306a36Sopenharmony_ci		ret = -EIO;
93962306a36Sopenharmony_ci	} else {
94062306a36Sopenharmony_ci		ret = get_unaligned_le16(buf + 1);
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci	kfree(buf);
94362306a36Sopenharmony_ci	return ret;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci/***********************************************************************
94762306a36Sopenharmony_ci * Program the tablet into either absolute or relative mode.
94862306a36Sopenharmony_ci * We also get information about the tablet's size.
94962306a36Sopenharmony_ci */
95062306a36Sopenharmony_cistatic int aiptek_program_tablet(struct aiptek *aiptek)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	int ret;
95362306a36Sopenharmony_ci	/* Execute Resolution500LPI */
95462306a36Sopenharmony_ci	if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0)
95562306a36Sopenharmony_ci		return ret;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* Query getModelCode */
95862306a36Sopenharmony_ci	if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0)
95962306a36Sopenharmony_ci		return ret;
96062306a36Sopenharmony_ci	aiptek->features.modelCode = ret & 0xff;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* Query getODMCode */
96362306a36Sopenharmony_ci	if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0)
96462306a36Sopenharmony_ci		return ret;
96562306a36Sopenharmony_ci	aiptek->features.odmCode = ret;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* Query getFirmwareCode */
96862306a36Sopenharmony_ci	if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0)
96962306a36Sopenharmony_ci		return ret;
97062306a36Sopenharmony_ci	aiptek->features.firmwareCode = ret;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* Query getXextension */
97362306a36Sopenharmony_ci	if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
97462306a36Sopenharmony_ci		return ret;
97562306a36Sopenharmony_ci	input_set_abs_params(aiptek->inputdev, ABS_X, 0, ret - 1, 0, 0);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* Query getYextension */
97862306a36Sopenharmony_ci	if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
97962306a36Sopenharmony_ci		return ret;
98062306a36Sopenharmony_ci	input_set_abs_params(aiptek->inputdev, ABS_Y, 0, ret - 1, 0, 0);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Query getPressureLevels */
98362306a36Sopenharmony_ci	if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
98462306a36Sopenharmony_ci		return ret;
98562306a36Sopenharmony_ci	input_set_abs_params(aiptek->inputdev, ABS_PRESSURE, 0, ret - 1, 0, 0);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/* Depending on whether we are in absolute or relative mode, we will
98862306a36Sopenharmony_ci	 * do a switchToTablet(absolute) or switchToMouse(relative) command.
98962306a36Sopenharmony_ci	 */
99062306a36Sopenharmony_ci	if (aiptek->curSetting.coordinateMode ==
99162306a36Sopenharmony_ci	    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
99262306a36Sopenharmony_ci		/* Execute switchToTablet */
99362306a36Sopenharmony_ci		if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) {
99462306a36Sopenharmony_ci			return ret;
99562306a36Sopenharmony_ci		}
99662306a36Sopenharmony_ci	} else {
99762306a36Sopenharmony_ci		/* Execute switchToMouse */
99862306a36Sopenharmony_ci		if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) {
99962306a36Sopenharmony_ci			return ret;
100062306a36Sopenharmony_ci		}
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	/* Enable the macro keys */
100462306a36Sopenharmony_ci	if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
100562306a36Sopenharmony_ci		return ret;
100662306a36Sopenharmony_ci#if 0
100762306a36Sopenharmony_ci	/* Execute FilterOn */
100862306a36Sopenharmony_ci	if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0)
100962306a36Sopenharmony_ci		return ret;
101062306a36Sopenharmony_ci#endif
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/* Execute AutoGainOn */
101362306a36Sopenharmony_ci	if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0)
101462306a36Sopenharmony_ci		return ret;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/* Reset the eventCount, so we track events from last (re)programming
101762306a36Sopenharmony_ci	 */
101862306a36Sopenharmony_ci	aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA;
101962306a36Sopenharmony_ci	aiptek->eventCount = 0;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	return 0;
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci/***********************************************************************
102562306a36Sopenharmony_ci * Sysfs functions. Sysfs prefers that individually-tunable parameters
102662306a36Sopenharmony_ci * exist in their separate pseudo-files. Summary data that is immutable
102762306a36Sopenharmony_ci * may exist in a singular file so long as you don't define a writeable
102862306a36Sopenharmony_ci * interface.
102962306a36Sopenharmony_ci */
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci/***********************************************************************
103262306a36Sopenharmony_ci * support the 'size' file -- display support
103362306a36Sopenharmony_ci */
103462306a36Sopenharmony_cistatic ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	return sysfs_emit(buf, "%dx%d\n",
103962306a36Sopenharmony_ci			  input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
104062306a36Sopenharmony_ci			  input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci/* These structs define the sysfs files, param #1 is the name of the
104462306a36Sopenharmony_ci * file, param 2 is the file permissions, param 3 & 4 are to the
104562306a36Sopenharmony_ci * output generator and input parser routines. Absence of a routine is
104662306a36Sopenharmony_ci * permitted -- it only means can't either 'cat' the file, or send data
104762306a36Sopenharmony_ci * to it.
104862306a36Sopenharmony_ci */
104962306a36Sopenharmony_cistatic DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci/***********************************************************************
105262306a36Sopenharmony_ci * support routines for the 'pointer_mode' file. Note that this file
105362306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
105462306a36Sopenharmony_ci */
105562306a36Sopenharmony_cistatic struct aiptek_map pointer_mode_map[] = {
105662306a36Sopenharmony_ci	{ "stylus",	AIPTEK_POINTER_ONLY_STYLUS_MODE },
105762306a36Sopenharmony_ci	{ "mouse",	AIPTEK_POINTER_ONLY_MOUSE_MODE },
105862306a36Sopenharmony_ci	{ "either",	AIPTEK_POINTER_EITHER_MODE },
105962306a36Sopenharmony_ci	{ NULL,		AIPTEK_INVALID_VALUE }
106062306a36Sopenharmony_ci};
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(pointer_mode_map,
106762306a36Sopenharmony_ci						      aiptek->curSetting.pointerMode));
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic ssize_t
107162306a36Sopenharmony_cistore_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
107462306a36Sopenharmony_ci	int new_mode = map_str_to_val(pointer_mode_map, buf, count);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (new_mode == AIPTEK_INVALID_VALUE)
107762306a36Sopenharmony_ci		return -EINVAL;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	aiptek->newSetting.pointerMode = new_mode;
108062306a36Sopenharmony_ci	return count;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic DEVICE_ATTR(pointer_mode,
108462306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
108562306a36Sopenharmony_ci		   show_tabletPointerMode, store_tabletPointerMode);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci/***********************************************************************
108862306a36Sopenharmony_ci * support routines for the 'coordinate_mode' file. Note that this file
108962306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
109062306a36Sopenharmony_ci */
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_cistatic struct aiptek_map coordinate_mode_map[] = {
109362306a36Sopenharmony_ci	{ "absolute",	AIPTEK_COORDINATE_ABSOLUTE_MODE },
109462306a36Sopenharmony_ci	{ "relative",	AIPTEK_COORDINATE_RELATIVE_MODE },
109562306a36Sopenharmony_ci	{ NULL,		AIPTEK_INVALID_VALUE }
109662306a36Sopenharmony_ci};
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(coordinate_mode_map,
110362306a36Sopenharmony_ci						      aiptek->curSetting.coordinateMode));
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic ssize_t
110762306a36Sopenharmony_cistore_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
111062306a36Sopenharmony_ci	int new_mode = map_str_to_val(coordinate_mode_map, buf, count);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (new_mode == AIPTEK_INVALID_VALUE)
111362306a36Sopenharmony_ci		return -EINVAL;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	aiptek->newSetting.coordinateMode = new_mode;
111662306a36Sopenharmony_ci	return count;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic DEVICE_ATTR(coordinate_mode,
112062306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
112162306a36Sopenharmony_ci		   show_tabletCoordinateMode, store_tabletCoordinateMode);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci/***********************************************************************
112462306a36Sopenharmony_ci * support routines for the 'tool_mode' file. Note that this file
112562306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic struct aiptek_map tool_mode_map[] = {
112962306a36Sopenharmony_ci	{ "mouse",	AIPTEK_TOOL_BUTTON_MOUSE_MODE },
113062306a36Sopenharmony_ci	{ "eraser",	AIPTEK_TOOL_BUTTON_ERASER_MODE },
113162306a36Sopenharmony_ci	{ "pencil",	AIPTEK_TOOL_BUTTON_PENCIL_MODE },
113262306a36Sopenharmony_ci	{ "pen",	AIPTEK_TOOL_BUTTON_PEN_MODE },
113362306a36Sopenharmony_ci	{ "brush",	AIPTEK_TOOL_BUTTON_BRUSH_MODE },
113462306a36Sopenharmony_ci	{ "airbrush",	AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE },
113562306a36Sopenharmony_ci	{ "lens",	AIPTEK_TOOL_BUTTON_LENS_MODE },
113662306a36Sopenharmony_ci	{ NULL,		AIPTEK_INVALID_VALUE }
113762306a36Sopenharmony_ci};
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(tool_mode_map,
114462306a36Sopenharmony_ci						      aiptek->curSetting.toolMode));
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic ssize_t
114862306a36Sopenharmony_cistore_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
115162306a36Sopenharmony_ci	int new_mode = map_str_to_val(tool_mode_map, buf, count);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (new_mode == AIPTEK_INVALID_VALUE)
115462306a36Sopenharmony_ci		return -EINVAL;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	aiptek->newSetting.toolMode = new_mode;
115762306a36Sopenharmony_ci	return count;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic DEVICE_ATTR(tool_mode,
116162306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
116262306a36Sopenharmony_ci		   show_tabletToolMode, store_tabletToolMode);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci/***********************************************************************
116562306a36Sopenharmony_ci * support routines for the 'xtilt' file. Note that this file
116662306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
116762306a36Sopenharmony_ci */
116862306a36Sopenharmony_cistatic ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
117362306a36Sopenharmony_ci		return sysfs_emit(buf, "disable\n");
117462306a36Sopenharmony_ci	} else {
117562306a36Sopenharmony_ci		return sysfs_emit(buf, "%d\n", aiptek->curSetting.xTilt);
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic ssize_t
118062306a36Sopenharmony_cistore_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
118362306a36Sopenharmony_ci	int x;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (kstrtoint(buf, 10, &x)) {
118662306a36Sopenharmony_ci		size_t len = buf[count - 1] == '\n' ? count - 1 : count;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		if (strncmp(buf, "disable", len))
118962306a36Sopenharmony_ci			return -EINVAL;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE;
119262306a36Sopenharmony_ci	} else {
119362306a36Sopenharmony_ci		if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX)
119462306a36Sopenharmony_ci			return -EINVAL;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		aiptek->newSetting.xTilt = x;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return count;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic DEVICE_ATTR(xtilt,
120362306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR, show_tabletXtilt, store_tabletXtilt);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci/***********************************************************************
120662306a36Sopenharmony_ci * support routines for the 'ytilt' file. Note that this file
120762306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
120862306a36Sopenharmony_ci */
120962306a36Sopenharmony_cistatic ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
121462306a36Sopenharmony_ci		return sysfs_emit(buf, "disable\n");
121562306a36Sopenharmony_ci	} else {
121662306a36Sopenharmony_ci		return sysfs_emit(buf, "%d\n", aiptek->curSetting.yTilt);
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic ssize_t
122162306a36Sopenharmony_cistore_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
122462306a36Sopenharmony_ci	int y;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (kstrtoint(buf, 10, &y)) {
122762306a36Sopenharmony_ci		size_t len = buf[count - 1] == '\n' ? count - 1 : count;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci		if (strncmp(buf, "disable", len))
123062306a36Sopenharmony_ci			return -EINVAL;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci		aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE;
123362306a36Sopenharmony_ci	} else {
123462306a36Sopenharmony_ci		if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX)
123562306a36Sopenharmony_ci			return -EINVAL;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		aiptek->newSetting.yTilt = y;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	return count;
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic DEVICE_ATTR(ytilt,
124462306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR, show_tabletYtilt, store_tabletYtilt);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci/***********************************************************************
124762306a36Sopenharmony_ci * support routines for the 'jitter' file. Note that this file
124862306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
124962306a36Sopenharmony_ci */
125062306a36Sopenharmony_cistatic ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", aiptek->curSetting.jitterDelay);
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_cistatic ssize_t
125862306a36Sopenharmony_cistore_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
126162306a36Sopenharmony_ci	int err, j;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	err = kstrtoint(buf, 10, &j);
126462306a36Sopenharmony_ci	if (err)
126562306a36Sopenharmony_ci		return err;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	aiptek->newSetting.jitterDelay = j;
126862306a36Sopenharmony_ci	return count;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic DEVICE_ATTR(jitter,
127262306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
127362306a36Sopenharmony_ci		   show_tabletJitterDelay, store_tabletJitterDelay);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci/***********************************************************************
127662306a36Sopenharmony_ci * support routines for the 'delay' file. Note that this file
127762306a36Sopenharmony_ci * both displays current setting and allows reprogramming.
127862306a36Sopenharmony_ci */
127962306a36Sopenharmony_cistatic ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", aiptek->curSetting.programmableDelay);
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic ssize_t
128762306a36Sopenharmony_cistore_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
129062306a36Sopenharmony_ci	int err, d;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	err = kstrtoint(buf, 10, &d);
129362306a36Sopenharmony_ci	if (err)
129462306a36Sopenharmony_ci		return err;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	aiptek->newSetting.programmableDelay = d;
129762306a36Sopenharmony_ci	return count;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic DEVICE_ATTR(delay,
130162306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
130262306a36Sopenharmony_ci		   show_tabletProgrammableDelay, store_tabletProgrammableDelay);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci/***********************************************************************
130562306a36Sopenharmony_ci * support routines for the 'event_count' file. Note that this file
130662306a36Sopenharmony_ci * only displays current setting.
130762306a36Sopenharmony_ci */
130862306a36Sopenharmony_cistatic ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	return sysfs_emit(buf, "%ld\n", aiptek->eventCount);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci/***********************************************************************
131862306a36Sopenharmony_ci * support routines for the 'diagnostic' file. Note that this file
131962306a36Sopenharmony_ci * only displays current setting.
132062306a36Sopenharmony_ci */
132162306a36Sopenharmony_cistatic ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
132462306a36Sopenharmony_ci	char *retMsg;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	switch (aiptek->diagnostic) {
132762306a36Sopenharmony_ci	case AIPTEK_DIAGNOSTIC_NA:
132862306a36Sopenharmony_ci		retMsg = "no errors\n";
132962306a36Sopenharmony_ci		break;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE:
133262306a36Sopenharmony_ci		retMsg = "Error: receiving relative reports\n";
133362306a36Sopenharmony_ci		break;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE:
133662306a36Sopenharmony_ci		retMsg = "Error: receiving absolute reports\n";
133762306a36Sopenharmony_ci		break;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED:
134062306a36Sopenharmony_ci		if (aiptek->curSetting.pointerMode ==
134162306a36Sopenharmony_ci		    AIPTEK_POINTER_ONLY_MOUSE_MODE) {
134262306a36Sopenharmony_ci			retMsg = "Error: receiving stylus reports\n";
134362306a36Sopenharmony_ci		} else {
134462306a36Sopenharmony_ci			retMsg = "Error: receiving mouse reports\n";
134562306a36Sopenharmony_ci		}
134662306a36Sopenharmony_ci		break;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	default:
134962306a36Sopenharmony_ci		return 0;
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci	return sysfs_emit(buf, retMsg);
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cistatic DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci/***********************************************************************
135762306a36Sopenharmony_ci * support routines for the 'stylus_upper' file. Note that this file
135862306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
135962306a36Sopenharmony_ci */
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic struct aiptek_map stylus_button_map[] = {
136262306a36Sopenharmony_ci	{ "upper",	AIPTEK_STYLUS_UPPER_BUTTON },
136362306a36Sopenharmony_ci	{ "lower",	AIPTEK_STYLUS_LOWER_BUTTON },
136462306a36Sopenharmony_ci	{ NULL,		AIPTEK_INVALID_VALUE }
136562306a36Sopenharmony_ci};
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map,
137262306a36Sopenharmony_ci						      aiptek->curSetting.stylusButtonUpper));
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic ssize_t
137662306a36Sopenharmony_cistore_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
137962306a36Sopenharmony_ci	int new_button = map_str_to_val(stylus_button_map, buf, count);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	if (new_button == AIPTEK_INVALID_VALUE)
138262306a36Sopenharmony_ci		return -EINVAL;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	aiptek->newSetting.stylusButtonUpper = new_button;
138562306a36Sopenharmony_ci	return count;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic DEVICE_ATTR(stylus_upper,
138962306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
139062306a36Sopenharmony_ci		   show_tabletStylusUpper, store_tabletStylusUpper);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci/***********************************************************************
139362306a36Sopenharmony_ci * support routines for the 'stylus_lower' file. Note that this file
139462306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
139562306a36Sopenharmony_ci */
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cistatic ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf)
139862306a36Sopenharmony_ci{
139962306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map,
140262306a36Sopenharmony_ci						      aiptek->curSetting.stylusButtonLower));
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic ssize_t
140662306a36Sopenharmony_cistore_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
140962306a36Sopenharmony_ci	int new_button = map_str_to_val(stylus_button_map, buf, count);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (new_button == AIPTEK_INVALID_VALUE)
141262306a36Sopenharmony_ci		return -EINVAL;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	aiptek->newSetting.stylusButtonLower = new_button;
141562306a36Sopenharmony_ci	return count;
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic DEVICE_ATTR(stylus_lower,
141962306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
142062306a36Sopenharmony_ci		   show_tabletStylusLower, store_tabletStylusLower);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci/***********************************************************************
142362306a36Sopenharmony_ci * support routines for the 'mouse_left' file. Note that this file
142462306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
142562306a36Sopenharmony_ci */
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic struct aiptek_map mouse_button_map[] = {
142862306a36Sopenharmony_ci	{ "left",	AIPTEK_MOUSE_LEFT_BUTTON },
142962306a36Sopenharmony_ci	{ "middle",	AIPTEK_MOUSE_MIDDLE_BUTTON },
143062306a36Sopenharmony_ci	{ "right",	AIPTEK_MOUSE_RIGHT_BUTTON },
143162306a36Sopenharmony_ci	{ NULL,		AIPTEK_INVALID_VALUE }
143262306a36Sopenharmony_ci};
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
143962306a36Sopenharmony_ci						      aiptek->curSetting.mouseButtonLeft));
144062306a36Sopenharmony_ci}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic ssize_t
144362306a36Sopenharmony_cistore_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
144462306a36Sopenharmony_ci{
144562306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
144662306a36Sopenharmony_ci	int new_button = map_str_to_val(mouse_button_map, buf, count);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	if (new_button == AIPTEK_INVALID_VALUE)
144962306a36Sopenharmony_ci		return -EINVAL;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	aiptek->newSetting.mouseButtonLeft = new_button;
145262306a36Sopenharmony_ci	return count;
145362306a36Sopenharmony_ci}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cistatic DEVICE_ATTR(mouse_left,
145662306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
145762306a36Sopenharmony_ci		   show_tabletMouseLeft, store_tabletMouseLeft);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci/***********************************************************************
146062306a36Sopenharmony_ci * support routines for the 'mouse_middle' file. Note that this file
146162306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
146262306a36Sopenharmony_ci */
146362306a36Sopenharmony_cistatic ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf)
146462306a36Sopenharmony_ci{
146562306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
146862306a36Sopenharmony_ci						      aiptek->curSetting.mouseButtonMiddle));
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_cistatic ssize_t
147262306a36Sopenharmony_cistore_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
147562306a36Sopenharmony_ci	int new_button = map_str_to_val(mouse_button_map, buf, count);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (new_button == AIPTEK_INVALID_VALUE)
147862306a36Sopenharmony_ci		return -EINVAL;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	aiptek->newSetting.mouseButtonMiddle = new_button;
148162306a36Sopenharmony_ci	return count;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic DEVICE_ATTR(mouse_middle,
148562306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
148662306a36Sopenharmony_ci		   show_tabletMouseMiddle, store_tabletMouseMiddle);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci/***********************************************************************
148962306a36Sopenharmony_ci * support routines for the 'mouse_right' file. Note that this file
149062306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
149162306a36Sopenharmony_ci */
149262306a36Sopenharmony_cistatic ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
149762306a36Sopenharmony_ci						      aiptek->curSetting.mouseButtonRight));
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic ssize_t
150162306a36Sopenharmony_cistore_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
150462306a36Sopenharmony_ci	int new_button = map_str_to_val(mouse_button_map, buf, count);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (new_button == AIPTEK_INVALID_VALUE)
150762306a36Sopenharmony_ci		return -EINVAL;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	aiptek->newSetting.mouseButtonRight = new_button;
151062306a36Sopenharmony_ci	return count;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic DEVICE_ATTR(mouse_right,
151462306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR,
151562306a36Sopenharmony_ci		   show_tabletMouseRight, store_tabletMouseRight);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci/***********************************************************************
151862306a36Sopenharmony_ci * support routines for the 'wheel' file. Note that this file
151962306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
152062306a36Sopenharmony_ci */
152162306a36Sopenharmony_cistatic ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
152662306a36Sopenharmony_ci		return sysfs_emit(buf, "disable\n");
152762306a36Sopenharmony_ci	} else {
152862306a36Sopenharmony_ci		return sysfs_emit(buf, "%d\n", aiptek->curSetting.wheel);
152962306a36Sopenharmony_ci	}
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic ssize_t
153362306a36Sopenharmony_cistore_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
153662306a36Sopenharmony_ci	int err, w;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	err = kstrtoint(buf, 10, &w);
153962306a36Sopenharmony_ci	if (err)
154062306a36Sopenharmony_ci		return err;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	aiptek->newSetting.wheel = w;
154362306a36Sopenharmony_ci	return count;
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_cistatic DEVICE_ATTR(wheel,
154762306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR, show_tabletWheel, store_tabletWheel);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci/***********************************************************************
155062306a36Sopenharmony_ci * support routines for the 'execute' file. Note that this file
155162306a36Sopenharmony_ci * both displays current setting and allows for setting changing.
155262306a36Sopenharmony_ci */
155362306a36Sopenharmony_cistatic ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	/* There is nothing useful to display, so a one-line manual
155662306a36Sopenharmony_ci	 * is in order...
155762306a36Sopenharmony_ci	 */
155862306a36Sopenharmony_ci	return sysfs_emit(buf, "Write anything to this file to program your tablet.\n");
155962306a36Sopenharmony_ci}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic ssize_t
156262306a36Sopenharmony_cistore_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	/* We do not care what you write to this file. Merely the action
156762306a36Sopenharmony_ci	 * of writing to this file triggers a tablet reprogramming.
156862306a36Sopenharmony_ci	 */
156962306a36Sopenharmony_ci	memcpy(&aiptek->curSetting, &aiptek->newSetting,
157062306a36Sopenharmony_ci	       sizeof(struct aiptek_settings));
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (aiptek_program_tablet(aiptek) < 0)
157362306a36Sopenharmony_ci		return -EIO;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	return count;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic DEVICE_ATTR(execute,
157962306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR, show_tabletExecute, store_tabletExecute);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci/***********************************************************************
158262306a36Sopenharmony_ci * support routines for the 'odm_code' file. Note that this file
158362306a36Sopenharmony_ci * only displays current setting.
158462306a36Sopenharmony_ci */
158562306a36Sopenharmony_cistatic ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf)
158662306a36Sopenharmony_ci{
158762306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%04x\n", aiptek->features.odmCode);
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cistatic DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci/***********************************************************************
159562306a36Sopenharmony_ci * support routines for the 'model_code' file. Note that this file
159662306a36Sopenharmony_ci * only displays current setting.
159762306a36Sopenharmony_ci */
159862306a36Sopenharmony_cistatic ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%04x\n", aiptek->features.modelCode);
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci/***********************************************************************
160862306a36Sopenharmony_ci * support routines for the 'firmware_code' file. Note that this file
160962306a36Sopenharmony_ci * only displays current setting.
161062306a36Sopenharmony_ci */
161162306a36Sopenharmony_cistatic ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf)
161262306a36Sopenharmony_ci{
161362306a36Sopenharmony_ci	struct aiptek *aiptek = dev_get_drvdata(dev);
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	return sysfs_emit(buf, "%04x\n", aiptek->features.firmwareCode);
161662306a36Sopenharmony_ci}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_cistatic DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic struct attribute *aiptek_dev_attrs[] = {
162162306a36Sopenharmony_ci	&dev_attr_size.attr,
162262306a36Sopenharmony_ci	&dev_attr_pointer_mode.attr,
162362306a36Sopenharmony_ci	&dev_attr_coordinate_mode.attr,
162462306a36Sopenharmony_ci	&dev_attr_tool_mode.attr,
162562306a36Sopenharmony_ci	&dev_attr_xtilt.attr,
162662306a36Sopenharmony_ci	&dev_attr_ytilt.attr,
162762306a36Sopenharmony_ci	&dev_attr_jitter.attr,
162862306a36Sopenharmony_ci	&dev_attr_delay.attr,
162962306a36Sopenharmony_ci	&dev_attr_event_count.attr,
163062306a36Sopenharmony_ci	&dev_attr_diagnostic.attr,
163162306a36Sopenharmony_ci	&dev_attr_odm_code.attr,
163262306a36Sopenharmony_ci	&dev_attr_model_code.attr,
163362306a36Sopenharmony_ci	&dev_attr_firmware_code.attr,
163462306a36Sopenharmony_ci	&dev_attr_stylus_lower.attr,
163562306a36Sopenharmony_ci	&dev_attr_stylus_upper.attr,
163662306a36Sopenharmony_ci	&dev_attr_mouse_left.attr,
163762306a36Sopenharmony_ci	&dev_attr_mouse_middle.attr,
163862306a36Sopenharmony_ci	&dev_attr_mouse_right.attr,
163962306a36Sopenharmony_ci	&dev_attr_wheel.attr,
164062306a36Sopenharmony_ci	&dev_attr_execute.attr,
164162306a36Sopenharmony_ci	NULL
164262306a36Sopenharmony_ci};
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ciATTRIBUTE_GROUPS(aiptek_dev);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci/***********************************************************************
164762306a36Sopenharmony_ci * This routine is called when a tablet has been identified. It basically
164862306a36Sopenharmony_ci * sets up the tablet and the driver's internal structures.
164962306a36Sopenharmony_ci */
165062306a36Sopenharmony_cistatic int
165162306a36Sopenharmony_ciaiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(intf);
165462306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
165562306a36Sopenharmony_ci	struct aiptek *aiptek;
165662306a36Sopenharmony_ci	struct input_dev *inputdev;
165762306a36Sopenharmony_ci	int i;
165862306a36Sopenharmony_ci	int speeds[] = { 0,
165962306a36Sopenharmony_ci		AIPTEK_PROGRAMMABLE_DELAY_50,
166062306a36Sopenharmony_ci		AIPTEK_PROGRAMMABLE_DELAY_400,
166162306a36Sopenharmony_ci		AIPTEK_PROGRAMMABLE_DELAY_25,
166262306a36Sopenharmony_ci		AIPTEK_PROGRAMMABLE_DELAY_100,
166362306a36Sopenharmony_ci		AIPTEK_PROGRAMMABLE_DELAY_200,
166462306a36Sopenharmony_ci		AIPTEK_PROGRAMMABLE_DELAY_300
166562306a36Sopenharmony_ci	};
166662306a36Sopenharmony_ci	int err = -ENOMEM;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	/* programmableDelay is where the command-line specified
166962306a36Sopenharmony_ci	 * delay is kept. We make it the first element of speeds[],
167062306a36Sopenharmony_ci	 * so therefore, your override speed is tried first, then the
167162306a36Sopenharmony_ci	 * remainder. Note that the default value of 400ms will be tried
167262306a36Sopenharmony_ci	 * if you do not specify any command line parameter.
167362306a36Sopenharmony_ci	 */
167462306a36Sopenharmony_ci	speeds[0] = programmableDelay;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL);
167762306a36Sopenharmony_ci	inputdev = input_allocate_device();
167862306a36Sopenharmony_ci	if (!aiptek || !inputdev) {
167962306a36Sopenharmony_ci		dev_warn(&intf->dev,
168062306a36Sopenharmony_ci			 "cannot allocate memory or input device\n");
168162306a36Sopenharmony_ci		goto fail1;
168262306a36Sopenharmony_ci        }
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	aiptek->data = usb_alloc_coherent(usbdev, AIPTEK_PACKET_LENGTH,
168562306a36Sopenharmony_ci					  GFP_KERNEL, &aiptek->data_dma);
168662306a36Sopenharmony_ci        if (!aiptek->data) {
168762306a36Sopenharmony_ci		dev_warn(&intf->dev, "cannot allocate usb buffer\n");
168862306a36Sopenharmony_ci		goto fail1;
168962306a36Sopenharmony_ci	}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	aiptek->urb = usb_alloc_urb(0, GFP_KERNEL);
169262306a36Sopenharmony_ci	if (!aiptek->urb) {
169362306a36Sopenharmony_ci	        dev_warn(&intf->dev, "cannot allocate urb\n");
169462306a36Sopenharmony_ci		goto fail2;
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	aiptek->inputdev = inputdev;
169862306a36Sopenharmony_ci	aiptek->intf = intf;
169962306a36Sopenharmony_ci	aiptek->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
170062306a36Sopenharmony_ci	aiptek->inDelay = 0;
170162306a36Sopenharmony_ci	aiptek->endDelay = 0;
170262306a36Sopenharmony_ci	aiptek->previousJitterable = 0;
170362306a36Sopenharmony_ci	aiptek->lastMacro = -1;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	/* Set up the curSettings struct. Said struct contains the current
170662306a36Sopenharmony_ci	 * programmable parameters. The newSetting struct contains changes
170762306a36Sopenharmony_ci	 * the user makes to the settings via the sysfs interface. Those
170862306a36Sopenharmony_ci	 * changes are not "committed" to curSettings until the user
170962306a36Sopenharmony_ci	 * writes to the sysfs/.../execute file.
171062306a36Sopenharmony_ci	 */
171162306a36Sopenharmony_ci	aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
171262306a36Sopenharmony_ci	aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE;
171362306a36Sopenharmony_ci	aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
171462306a36Sopenharmony_ci	aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE;
171562306a36Sopenharmony_ci	aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE;
171662306a36Sopenharmony_ci	aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
171762306a36Sopenharmony_ci	aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON;
171862306a36Sopenharmony_ci	aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
171962306a36Sopenharmony_ci	aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON;
172062306a36Sopenharmony_ci	aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON;
172162306a36Sopenharmony_ci	aiptek->curSetting.jitterDelay = jitterDelay;
172262306a36Sopenharmony_ci	aiptek->curSetting.programmableDelay = programmableDelay;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	/* Both structs should have equivalent settings
172562306a36Sopenharmony_ci	 */
172662306a36Sopenharmony_ci	aiptek->newSetting = aiptek->curSetting;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	/* Determine the usb devices' physical path.
172962306a36Sopenharmony_ci	 * Asketh not why we always pretend we're using "../input0",
173062306a36Sopenharmony_ci	 * but I suspect this will have to be refactored one
173162306a36Sopenharmony_ci	 * day if a single USB device can be a keyboard & a mouse
173262306a36Sopenharmony_ci	 * & a tablet, and the inputX number actually will tell
173362306a36Sopenharmony_ci	 * us something...
173462306a36Sopenharmony_ci	 */
173562306a36Sopenharmony_ci	usb_make_path(usbdev, aiptek->features.usbPath,
173662306a36Sopenharmony_ci			sizeof(aiptek->features.usbPath));
173762306a36Sopenharmony_ci	strlcat(aiptek->features.usbPath, "/input0",
173862306a36Sopenharmony_ci		sizeof(aiptek->features.usbPath));
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	/* Set up client data, pointers to open and close routines
174162306a36Sopenharmony_ci	 * for the input device.
174262306a36Sopenharmony_ci	 */
174362306a36Sopenharmony_ci	inputdev->name = "Aiptek";
174462306a36Sopenharmony_ci	inputdev->phys = aiptek->features.usbPath;
174562306a36Sopenharmony_ci	usb_to_input_id(usbdev, &inputdev->id);
174662306a36Sopenharmony_ci	inputdev->dev.parent = &intf->dev;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	input_set_drvdata(inputdev, aiptek);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	inputdev->open = aiptek_open;
175162306a36Sopenharmony_ci	inputdev->close = aiptek_close;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	/* Now program the capacities of the tablet, in terms of being
175462306a36Sopenharmony_ci	 * an input device.
175562306a36Sopenharmony_ci	 */
175662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eventTypes); ++i)
175762306a36Sopenharmony_ci	        __set_bit(eventTypes[i], inputdev->evbit);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(absEvents); ++i)
176062306a36Sopenharmony_ci	        __set_bit(absEvents[i], inputdev->absbit);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(relEvents); ++i)
176362306a36Sopenharmony_ci	        __set_bit(relEvents[i], inputdev->relbit);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	__set_bit(MSC_SERIAL, inputdev->mscbit);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	/* Set up key and button codes */
176862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i)
176962306a36Sopenharmony_ci		__set_bit(buttonEvents[i], inputdev->keybit);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
177262306a36Sopenharmony_ci		__set_bit(macroKeyEvents[i], inputdev->keybit);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	/*
177562306a36Sopenharmony_ci	 * Program the input device coordinate capacities. We do not yet
177662306a36Sopenharmony_ci	 * know what maximum X, Y, and Z values are, so we're putting fake
177762306a36Sopenharmony_ci	 * values in. Later, we'll ask the tablet to put in the correct
177862306a36Sopenharmony_ci	 * values.
177962306a36Sopenharmony_ci	 */
178062306a36Sopenharmony_ci	input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0);
178162306a36Sopenharmony_ci	input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0);
178262306a36Sopenharmony_ci	input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0);
178362306a36Sopenharmony_ci	input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
178462306a36Sopenharmony_ci	input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
178562306a36Sopenharmony_ci	input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0);
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	err = usb_find_common_endpoints(intf->cur_altsetting,
178862306a36Sopenharmony_ci					NULL, NULL, &endpoint, NULL);
178962306a36Sopenharmony_ci	if (err) {
179062306a36Sopenharmony_ci		dev_err(&intf->dev,
179162306a36Sopenharmony_ci			"interface has no int in endpoints, but must have minimum 1\n");
179262306a36Sopenharmony_ci		goto fail3;
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* Go set up our URB, which is called when the tablet receives
179662306a36Sopenharmony_ci	 * input.
179762306a36Sopenharmony_ci	 */
179862306a36Sopenharmony_ci	usb_fill_int_urb(aiptek->urb,
179962306a36Sopenharmony_ci			 usbdev,
180062306a36Sopenharmony_ci			 usb_rcvintpipe(usbdev,
180162306a36Sopenharmony_ci					endpoint->bEndpointAddress),
180262306a36Sopenharmony_ci			 aiptek->data, 8, aiptek_irq, aiptek,
180362306a36Sopenharmony_ci			 endpoint->bInterval);
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	aiptek->urb->transfer_dma = aiptek->data_dma;
180662306a36Sopenharmony_ci	aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	/* Program the tablet. This sets the tablet up in the mode
180962306a36Sopenharmony_ci	 * specified in newSetting, and also queries the tablet's
181062306a36Sopenharmony_ci	 * physical capacities.
181162306a36Sopenharmony_ci	 *
181262306a36Sopenharmony_ci	 * Sanity check: if a tablet doesn't like the slow programmatic
181362306a36Sopenharmony_ci	 * delay, we often get sizes of 0x0. Let's use that as an indicator
181462306a36Sopenharmony_ci	 * to try faster delays, up to 25 ms. If that logic fails, well, you'll
181562306a36Sopenharmony_ci	 * have to explain to us how your tablet thinks it's 0x0, and yet that's
181662306a36Sopenharmony_ci	 * not an error :-)
181762306a36Sopenharmony_ci	 */
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
182062306a36Sopenharmony_ci		aiptek->curSetting.programmableDelay = speeds[i];
182162306a36Sopenharmony_ci		(void)aiptek_program_tablet(aiptek);
182262306a36Sopenharmony_ci		if (input_abs_get_max(aiptek->inputdev, ABS_X) > 0) {
182362306a36Sopenharmony_ci			dev_info(&intf->dev,
182462306a36Sopenharmony_ci				 "Aiptek using %d ms programming speed\n",
182562306a36Sopenharmony_ci				 aiptek->curSetting.programmableDelay);
182662306a36Sopenharmony_ci			break;
182762306a36Sopenharmony_ci		}
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	/* Murphy says that some day someone will have a tablet that fails the
183162306a36Sopenharmony_ci	   above test. That's you, Frederic Rodrigo */
183262306a36Sopenharmony_ci	if (i == ARRAY_SIZE(speeds)) {
183362306a36Sopenharmony_ci		dev_info(&intf->dev,
183462306a36Sopenharmony_ci			 "Aiptek tried all speeds, no sane response\n");
183562306a36Sopenharmony_ci		err = -EINVAL;
183662306a36Sopenharmony_ci		goto fail3;
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	/* Associate this driver's struct with the usb interface.
184062306a36Sopenharmony_ci	 */
184162306a36Sopenharmony_ci	usb_set_intfdata(intf, aiptek);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	/* Register the tablet as an Input Device
184462306a36Sopenharmony_ci	 */
184562306a36Sopenharmony_ci	err = input_register_device(aiptek->inputdev);
184662306a36Sopenharmony_ci	if (err) {
184762306a36Sopenharmony_ci		dev_warn(&intf->dev,
184862306a36Sopenharmony_ci			 "input_register_device returned err: %d\n", err);
184962306a36Sopenharmony_ci		goto fail3;
185062306a36Sopenharmony_ci        }
185162306a36Sopenharmony_ci	return 0;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci fail3: usb_free_urb(aiptek->urb);
185462306a36Sopenharmony_ci fail2:	usb_free_coherent(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data,
185562306a36Sopenharmony_ci			  aiptek->data_dma);
185662306a36Sopenharmony_ci fail1: usb_set_intfdata(intf, NULL);
185762306a36Sopenharmony_ci	input_free_device(inputdev);
185862306a36Sopenharmony_ci	kfree(aiptek);
185962306a36Sopenharmony_ci	return err;
186062306a36Sopenharmony_ci}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci/***********************************************************************
186362306a36Sopenharmony_ci * Deal with tablet disconnecting from the system.
186462306a36Sopenharmony_ci */
186562306a36Sopenharmony_cistatic void aiptek_disconnect(struct usb_interface *intf)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	struct aiptek *aiptek = usb_get_intfdata(intf);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	/* Disassociate driver's struct with usb interface
187062306a36Sopenharmony_ci	 */
187162306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
187262306a36Sopenharmony_ci	if (aiptek != NULL) {
187362306a36Sopenharmony_ci		/* Free & unhook everything from the system.
187462306a36Sopenharmony_ci		 */
187562306a36Sopenharmony_ci		usb_kill_urb(aiptek->urb);
187662306a36Sopenharmony_ci		input_unregister_device(aiptek->inputdev);
187762306a36Sopenharmony_ci		usb_free_urb(aiptek->urb);
187862306a36Sopenharmony_ci		usb_free_coherent(interface_to_usbdev(intf),
187962306a36Sopenharmony_ci				  AIPTEK_PACKET_LENGTH,
188062306a36Sopenharmony_ci				  aiptek->data, aiptek->data_dma);
188162306a36Sopenharmony_ci		kfree(aiptek);
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_cistatic struct usb_driver aiptek_driver = {
188662306a36Sopenharmony_ci	.name = "aiptek",
188762306a36Sopenharmony_ci	.probe = aiptek_probe,
188862306a36Sopenharmony_ci	.disconnect = aiptek_disconnect,
188962306a36Sopenharmony_ci	.id_table = aiptek_ids,
189062306a36Sopenharmony_ci	.dev_groups = aiptek_dev_groups,
189162306a36Sopenharmony_ci};
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_cimodule_usb_driver(aiptek_driver);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ciMODULE_AUTHOR("Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen");
189662306a36Sopenharmony_ciMODULE_DESCRIPTION("Aiptek HyperPen USB Tablet Driver");
189762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_cimodule_param(programmableDelay, int, 0);
190062306a36Sopenharmony_ciMODULE_PARM_DESC(programmableDelay, "delay used during tablet programming");
190162306a36Sopenharmony_cimodule_param(jitterDelay, int, 0);
190262306a36Sopenharmony_ciMODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay");
1903