xref: /openbmc/linux/drivers/input/tablet/aiptek.c (revision 4104d13fe0194736393d97c88ee045fb689c783b)
1*4104d13fSDmitry Torokhov /*
2*4104d13fSDmitry Torokhov  *  Native support for the Aiptek HyperPen USB Tablets
3*4104d13fSDmitry Torokhov  *  (4000U/5000U/6000U/8000U/12000U)
4*4104d13fSDmitry Torokhov  *
5*4104d13fSDmitry Torokhov  *  Copyright (c) 2001      Chris Atenasio   <chris@crud.net>
6*4104d13fSDmitry Torokhov  *  Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
7*4104d13fSDmitry Torokhov  *
8*4104d13fSDmitry Torokhov  *  based on wacom.c by
9*4104d13fSDmitry Torokhov  *     Vojtech Pavlik      <vojtech@suse.cz>
10*4104d13fSDmitry Torokhov  *     Andreas Bach Aaen   <abach@stofanet.dk>
11*4104d13fSDmitry Torokhov  *     Clifford Wolf       <clifford@clifford.at>
12*4104d13fSDmitry Torokhov  *     Sam Mosel           <sam.mosel@computer.org>
13*4104d13fSDmitry Torokhov  *     James E. Blair      <corvus@gnu.org>
14*4104d13fSDmitry Torokhov  *     Daniel Egger        <egger@suse.de>
15*4104d13fSDmitry Torokhov  *
16*4104d13fSDmitry Torokhov  *  Many thanks to Oliver Kuechemann for his support.
17*4104d13fSDmitry Torokhov  *
18*4104d13fSDmitry Torokhov  *  ChangeLog:
19*4104d13fSDmitry Torokhov  *      v0.1 - Initial release
20*4104d13fSDmitry Torokhov  *      v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
21*4104d13fSDmitry Torokhov  *      v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
22*4104d13fSDmitry Torokhov  *             Released to Linux 2.4.19 and 2.5.x
23*4104d13fSDmitry Torokhov  *      v0.4 - Rewrote substantial portions of the code to deal with
24*4104d13fSDmitry Torokhov  *             corrected control sequences, timing, dynamic configuration,
25*4104d13fSDmitry Torokhov  *             support of 6000U - 12000U, procfs, and macro key support
26*4104d13fSDmitry Torokhov  *             (Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
27*4104d13fSDmitry Torokhov  *      v1.0 - Added support for diagnostic messages, count of messages
28*4104d13fSDmitry Torokhov  *             received from URB - Mar-8-2003, Bryan W. Headley
29*4104d13fSDmitry Torokhov  *      v1.1 - added support for tablet resolution, changed DV and proximity
30*4104d13fSDmitry Torokhov  *             some corrections - Jun-22-2003, martin schneebacher
31*4104d13fSDmitry Torokhov  *           - Added support for the sysfs interface, deprecating the
32*4104d13fSDmitry Torokhov  *             procfs interface for 2.5.x kernel. Also added support for
33*4104d13fSDmitry Torokhov  *             Wheel command. Bryan W. Headley July-15-2003.
34*4104d13fSDmitry Torokhov  *      v1.2 - Reworked jitter timer as a kernel thread.
35*4104d13fSDmitry Torokhov  *             Bryan W. Headley November-28-2003/Jan-10-2004.
36*4104d13fSDmitry Torokhov  *      v1.3 - Repaired issue of kernel thread going nuts on single-processor
37*4104d13fSDmitry Torokhov  *             machines, introduced programmableDelay as a command line
38*4104d13fSDmitry Torokhov  *             parameter. Feb 7 2004, Bryan W. Headley.
39*4104d13fSDmitry Torokhov  *      v1.4 - Re-wire jitter so it does not require a thread. Courtesy of
40*4104d13fSDmitry Torokhov  *             Rene van Paassen. Added reporting of physical pointer device
41*4104d13fSDmitry Torokhov  *             (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know
42*4104d13fSDmitry Torokhov  *             for reports 1, 6.)
43*4104d13fSDmitry Torokhov  *             what physical device reports for reports 1, 6.) Also enabled
44*4104d13fSDmitry Torokhov  *             MOUSE and LENS tool button modes. Renamed "rubber" to "eraser".
45*4104d13fSDmitry Torokhov  *             Feb 20, 2004, Bryan W. Headley.
46*4104d13fSDmitry Torokhov  *      v1.5 - Added previousJitterable, so we don't do jitter delay when the
47*4104d13fSDmitry Torokhov  *             user is holding a button down for periods of time.
48*4104d13fSDmitry Torokhov  *
49*4104d13fSDmitry Torokhov  * NOTE:
50*4104d13fSDmitry Torokhov  *      This kernel driver is augmented by the "Aiptek" XFree86 input
51*4104d13fSDmitry Torokhov  *      driver for your X server, as well as the Gaiptek GUI Front-end
52*4104d13fSDmitry Torokhov  *      "Tablet Manager".
53*4104d13fSDmitry Torokhov  *      These three products are highly interactive with one another,
54*4104d13fSDmitry Torokhov  *      so therefore it's easier to document them all as one subsystem.
55*4104d13fSDmitry Torokhov  *      Please visit the project's "home page", located at,
56*4104d13fSDmitry Torokhov  *      http://aiptektablet.sourceforge.net.
57*4104d13fSDmitry Torokhov  *
58*4104d13fSDmitry Torokhov  * This program is free software; you can redistribute it and/or modify
59*4104d13fSDmitry Torokhov  * it under the terms of the GNU General Public License as published by
60*4104d13fSDmitry Torokhov  * the Free Software Foundation; either version 2 of the License, or
61*4104d13fSDmitry Torokhov  * (at your option) any later version.
62*4104d13fSDmitry Torokhov  *
63*4104d13fSDmitry Torokhov  * This program is distributed in the hope that it will be useful,
64*4104d13fSDmitry Torokhov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
65*4104d13fSDmitry Torokhov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
66*4104d13fSDmitry Torokhov  * GNU General Public License for more details.
67*4104d13fSDmitry Torokhov  *
68*4104d13fSDmitry Torokhov  * You should have received a copy of the GNU General Public License
69*4104d13fSDmitry Torokhov  * along with this program; if not, write to the Free Software
70*4104d13fSDmitry Torokhov  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
71*4104d13fSDmitry Torokhov  */
72*4104d13fSDmitry Torokhov 
73*4104d13fSDmitry Torokhov #include <linux/jiffies.h>
74*4104d13fSDmitry Torokhov #include <linux/kernel.h>
75*4104d13fSDmitry Torokhov #include <linux/slab.h>
76*4104d13fSDmitry Torokhov #include <linux/module.h>
77*4104d13fSDmitry Torokhov #include <linux/init.h>
78*4104d13fSDmitry Torokhov #include <linux/usb/input.h>
79*4104d13fSDmitry Torokhov #include <asm/uaccess.h>
80*4104d13fSDmitry Torokhov #include <asm/unaligned.h>
81*4104d13fSDmitry Torokhov 
82*4104d13fSDmitry Torokhov /*
83*4104d13fSDmitry Torokhov  * Version Information
84*4104d13fSDmitry Torokhov  */
85*4104d13fSDmitry Torokhov #define DRIVER_VERSION "v1.5 (May-15-2004)"
86*4104d13fSDmitry Torokhov #define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio"
87*4104d13fSDmitry Torokhov #define DRIVER_DESC    "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
88*4104d13fSDmitry Torokhov 
89*4104d13fSDmitry Torokhov /*
90*4104d13fSDmitry Torokhov  * Aiptek status packet:
91*4104d13fSDmitry Torokhov  *
92*4104d13fSDmitry Torokhov  * (returned as Report 1 - relative coordinates from mouse and stylus)
93*4104d13fSDmitry Torokhov  *
94*4104d13fSDmitry Torokhov  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
95*4104d13fSDmitry Torokhov  * byte0   0     0     0     0     0     0     0     1
96*4104d13fSDmitry Torokhov  * byte1   0     0     0     0     0    BS2   BS    Tip
97*4104d13fSDmitry Torokhov  * byte2  X7    X6    X5    X4    X3    X2    X1    X0
98*4104d13fSDmitry Torokhov  * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
99*4104d13fSDmitry Torokhov  *
100*4104d13fSDmitry Torokhov  * (returned as Report 2 - absolute coordinates from the stylus)
101*4104d13fSDmitry Torokhov  *
102*4104d13fSDmitry Torokhov  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
103*4104d13fSDmitry Torokhov  * byte0   0     0     0     0     0     0     1     0
104*4104d13fSDmitry Torokhov  * byte1  X7    X6    X5    X4    X3    X2    X1    X0
105*4104d13fSDmitry Torokhov  * byte2  X15   X14   X13   X12   X11   X10   X9    X8
106*4104d13fSDmitry Torokhov  * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
107*4104d13fSDmitry Torokhov  * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
108*4104d13fSDmitry Torokhov  * byte5   *     *     *    BS2   BS1   Tip   IR    DV
109*4104d13fSDmitry Torokhov  * byte6  P7    P6    P5    P4    P3    P2    P1    P0
110*4104d13fSDmitry Torokhov  * byte7  P15   P14   P13   P12   P11   P10   P9    P8
111*4104d13fSDmitry Torokhov  *
112*4104d13fSDmitry Torokhov  * (returned as Report 3 - absolute coordinates from the mouse)
113*4104d13fSDmitry Torokhov  *
114*4104d13fSDmitry Torokhov  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
115*4104d13fSDmitry Torokhov  * byte0   0     0     0     0     0     0     1     0
116*4104d13fSDmitry Torokhov  * byte1  X7    X6    X5    X4    X3    X2    X1    X0
117*4104d13fSDmitry Torokhov  * byte2  X15   X14   X13   X12   X11   X10   X9    X8
118*4104d13fSDmitry Torokhov  * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
119*4104d13fSDmitry Torokhov  * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
120*4104d13fSDmitry Torokhov  * byte5   *     *     *    BS2   BS1   Tip   IR    DV
121*4104d13fSDmitry Torokhov  * byte6  P7    P6    P5    P4    P3    P2    P1    P0
122*4104d13fSDmitry Torokhov  * byte7  P15   P14   P13   P12   P11   P10   P9    P8
123*4104d13fSDmitry Torokhov  *
124*4104d13fSDmitry Torokhov  * (returned as Report 4 - macrokeys from the stylus)
125*4104d13fSDmitry Torokhov  *
126*4104d13fSDmitry Torokhov  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
127*4104d13fSDmitry Torokhov  * byte0   0     0     0     0     0     1     0     0
128*4104d13fSDmitry Torokhov  * byte1   0     0     0    BS2   BS    Tip   IR    DV
129*4104d13fSDmitry Torokhov  * byte2   0     0     0     0     0     0     1     0
130*4104d13fSDmitry Torokhov  * byte3   0     0     0    K4    K3    K2    K1    K0
131*4104d13fSDmitry Torokhov  * byte4  P7    P6    P5    P4    P3    P2    P1    P0
132*4104d13fSDmitry Torokhov  * byte5  P15   P14   P13   P12   P11   P10   P9    P8
133*4104d13fSDmitry Torokhov  *
134*4104d13fSDmitry Torokhov  * (returned as Report 5 - macrokeys from the mouse)
135*4104d13fSDmitry Torokhov  *
136*4104d13fSDmitry Torokhov  *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
137*4104d13fSDmitry Torokhov  * byte0   0     0     0     0     0     1     0     0
138*4104d13fSDmitry Torokhov  * byte1   0     0     0    BS2   BS    Tip   IR    DV
139*4104d13fSDmitry Torokhov  * byte2   0     0     0     0     0     0     1     0
140*4104d13fSDmitry Torokhov  * byte3   0     0     0    K4    K3    K2    K1    K0
141*4104d13fSDmitry Torokhov  * byte4  P7    P6    P5    P4    P3    P2    P1    P0
142*4104d13fSDmitry Torokhov  * byte5  P15   P14   P13   P12   P11   P10   P9    P8
143*4104d13fSDmitry Torokhov  *
144*4104d13fSDmitry Torokhov  * IR: In Range = Proximity on
145*4104d13fSDmitry Torokhov  * DV = Data Valid
146*4104d13fSDmitry Torokhov  * BS = Barrel Switch (as in, macro keys)
147*4104d13fSDmitry Torokhov  * BS2 also referred to as Tablet Pick
148*4104d13fSDmitry Torokhov  *
149*4104d13fSDmitry Torokhov  * Command Summary:
150*4104d13fSDmitry Torokhov  *
151*4104d13fSDmitry Torokhov  * Use report_type CONTROL (3)
152*4104d13fSDmitry Torokhov  * Use report_id   2
153*4104d13fSDmitry Torokhov  *
154*4104d13fSDmitry Torokhov  * Command/Data    Description     Return Bytes    Return Value
155*4104d13fSDmitry Torokhov  * 0x10/0x00       SwitchToMouse       0
156*4104d13fSDmitry Torokhov  * 0x10/0x01       SwitchToTablet      0
157*4104d13fSDmitry Torokhov  * 0x18/0x04       SetResolution       0
158*4104d13fSDmitry Torokhov  * 0x12/0xFF       AutoGainOn          0
159*4104d13fSDmitry Torokhov  * 0x17/0x00       FilterOn            0
160*4104d13fSDmitry Torokhov  * 0x01/0x00       GetXExtension       2           MaxX
161*4104d13fSDmitry Torokhov  * 0x01/0x01       GetYExtension       2           MaxY
162*4104d13fSDmitry Torokhov  * 0x02/0x00       GetModelCode        2           ModelCode = LOBYTE
163*4104d13fSDmitry Torokhov  * 0x03/0x00       GetODMCode          2           ODMCode
164*4104d13fSDmitry Torokhov  * 0x08/0x00       GetPressureLevels   2           =512
165*4104d13fSDmitry Torokhov  * 0x04/0x00       GetFirmwareVersion  2           Firmware Version
166*4104d13fSDmitry Torokhov  * 0x11/0x02       EnableMacroKeys     0
167*4104d13fSDmitry Torokhov  *
168*4104d13fSDmitry Torokhov  * To initialize the tablet:
169*4104d13fSDmitry Torokhov  *
170*4104d13fSDmitry Torokhov  * (1) Send Resolution500LPI (Command)
171*4104d13fSDmitry Torokhov  * (2) Query for Model code (Option Report)
172*4104d13fSDmitry Torokhov  * (3) Query for ODM code (Option Report)
173*4104d13fSDmitry Torokhov  * (4) Query for firmware (Option Report)
174*4104d13fSDmitry Torokhov  * (5) Query for GetXExtension (Option Report)
175*4104d13fSDmitry Torokhov  * (6) Query for GetYExtension (Option Report)
176*4104d13fSDmitry Torokhov  * (7) Query for GetPressureLevels (Option Report)
177*4104d13fSDmitry Torokhov  * (8) SwitchToTablet for Absolute coordinates, or
178*4104d13fSDmitry Torokhov  *     SwitchToMouse for Relative coordinates (Command)
179*4104d13fSDmitry Torokhov  * (9) EnableMacroKeys (Command)
180*4104d13fSDmitry Torokhov  * (10) FilterOn (Command)
181*4104d13fSDmitry Torokhov  * (11) AutoGainOn (Command)
182*4104d13fSDmitry Torokhov  *
183*4104d13fSDmitry Torokhov  * (Step 9 can be omitted, but you'll then have no function keys.)
184*4104d13fSDmitry Torokhov  */
185*4104d13fSDmitry Torokhov 
186*4104d13fSDmitry Torokhov #define USB_VENDOR_ID_AIPTEK				0x08ca
187*4104d13fSDmitry Torokhov #define USB_REQ_GET_REPORT				0x01
188*4104d13fSDmitry Torokhov #define USB_REQ_SET_REPORT				0x09
189*4104d13fSDmitry Torokhov 
190*4104d13fSDmitry Torokhov 	/* PointerMode codes
191*4104d13fSDmitry Torokhov 	 */
192*4104d13fSDmitry Torokhov #define AIPTEK_POINTER_ONLY_MOUSE_MODE			0
193*4104d13fSDmitry Torokhov #define AIPTEK_POINTER_ONLY_STYLUS_MODE			1
194*4104d13fSDmitry Torokhov #define AIPTEK_POINTER_EITHER_MODE			2
195*4104d13fSDmitry Torokhov 
196*4104d13fSDmitry Torokhov #define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a)		\
197*4104d13fSDmitry Torokhov 	(a == AIPTEK_POINTER_ONLY_MOUSE_MODE ||		\
198*4104d13fSDmitry Torokhov 	 a == AIPTEK_POINTER_EITHER_MODE)
199*4104d13fSDmitry Torokhov #define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a)		\
200*4104d13fSDmitry Torokhov 	(a == AIPTEK_POINTER_ONLY_STYLUS_MODE ||	\
201*4104d13fSDmitry Torokhov 	 a == AIPTEK_POINTER_EITHER_MODE)
202*4104d13fSDmitry Torokhov 
203*4104d13fSDmitry Torokhov 	/* CoordinateMode code
204*4104d13fSDmitry Torokhov 	 */
205*4104d13fSDmitry Torokhov #define AIPTEK_COORDINATE_RELATIVE_MODE			0
206*4104d13fSDmitry Torokhov #define AIPTEK_COORDINATE_ABSOLUTE_MODE			1
207*4104d13fSDmitry Torokhov 
208*4104d13fSDmitry Torokhov        /* XTilt and YTilt values
209*4104d13fSDmitry Torokhov         */
210*4104d13fSDmitry Torokhov #define AIPTEK_TILT_MIN					(-128)
211*4104d13fSDmitry Torokhov #define AIPTEK_TILT_MAX					127
212*4104d13fSDmitry Torokhov #define AIPTEK_TILT_DISABLE				(-10101)
213*4104d13fSDmitry Torokhov 
214*4104d13fSDmitry Torokhov 	/* Wheel values
215*4104d13fSDmitry Torokhov 	 */
216*4104d13fSDmitry Torokhov #define AIPTEK_WHEEL_MIN				0
217*4104d13fSDmitry Torokhov #define AIPTEK_WHEEL_MAX				1024
218*4104d13fSDmitry Torokhov #define AIPTEK_WHEEL_DISABLE				(-10101)
219*4104d13fSDmitry Torokhov 
220*4104d13fSDmitry Torokhov 	/* ToolCode values, which BTW are 0x140 .. 0x14f
221*4104d13fSDmitry Torokhov 	 * We have things set up such that if TOOL_BUTTON_FIRED_BIT is
222*4104d13fSDmitry Torokhov 	 * not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx.
223*4104d13fSDmitry Torokhov 	 *
224*4104d13fSDmitry Torokhov 	 * Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will
225*4104d13fSDmitry Torokhov 	 * get reset.
226*4104d13fSDmitry Torokhov 	 */
227*4104d13fSDmitry Torokhov #define TOOL_BUTTON(x)					((x) & 0x14f)
228*4104d13fSDmitry Torokhov #define TOOL_BUTTON_FIRED(x)				((x) & 0x200)
229*4104d13fSDmitry Torokhov #define TOOL_BUTTON_FIRED_BIT				0x200
230*4104d13fSDmitry Torokhov 	/* toolMode codes
231*4104d13fSDmitry Torokhov 	 */
232*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
233*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
234*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_PENCIL_MODE			BTN_TOOL_PENCIL
235*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_BRUSH_MODE			BTN_TOOL_BRUSH
236*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE		BTN_TOOL_AIRBRUSH
237*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_ERASER_MODE			BTN_TOOL_RUBBER
238*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_MOUSE_MODE			BTN_TOOL_MOUSE
239*4104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_LENS_MODE			BTN_TOOL_LENS
240*4104d13fSDmitry Torokhov 
241*4104d13fSDmitry Torokhov 	/* Diagnostic message codes
242*4104d13fSDmitry Torokhov 	 */
243*4104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_NA				0
244*4104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE	1
245*4104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE	2
246*4104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED		3
247*4104d13fSDmitry Torokhov 
248*4104d13fSDmitry Torokhov 	/* Time to wait (in ms) to help mask hand jittering
249*4104d13fSDmitry Torokhov 	 * when pressing the stylus buttons.
250*4104d13fSDmitry Torokhov 	 */
251*4104d13fSDmitry Torokhov #define AIPTEK_JITTER_DELAY_DEFAULT			50
252*4104d13fSDmitry Torokhov 
253*4104d13fSDmitry Torokhov 	/* Time to wait (in ms) in-between sending the tablet
254*4104d13fSDmitry Torokhov 	 * a command and beginning the process of reading the return
255*4104d13fSDmitry Torokhov 	 * sequence from the tablet.
256*4104d13fSDmitry Torokhov 	 */
257*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_25		25
258*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_50		50
259*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_100		100
260*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_200		200
261*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_300		300
262*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_400		400
263*4104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT	AIPTEK_PROGRAMMABLE_DELAY_400
264*4104d13fSDmitry Torokhov 
265*4104d13fSDmitry Torokhov 	/* Mouse button programming
266*4104d13fSDmitry Torokhov 	 */
267*4104d13fSDmitry Torokhov #define AIPTEK_MOUSE_LEFT_BUTTON		0x01
268*4104d13fSDmitry Torokhov #define AIPTEK_MOUSE_RIGHT_BUTTON		0x02
269*4104d13fSDmitry Torokhov #define AIPTEK_MOUSE_MIDDLE_BUTTON		0x04
270*4104d13fSDmitry Torokhov 
271*4104d13fSDmitry Torokhov 	/* Stylus button programming
272*4104d13fSDmitry Torokhov 	 */
273*4104d13fSDmitry Torokhov #define AIPTEK_STYLUS_LOWER_BUTTON		0x08
274*4104d13fSDmitry Torokhov #define AIPTEK_STYLUS_UPPER_BUTTON		0x10
275*4104d13fSDmitry Torokhov 
276*4104d13fSDmitry Torokhov 	/* Length of incoming packet from the tablet
277*4104d13fSDmitry Torokhov 	 */
278*4104d13fSDmitry Torokhov #define AIPTEK_PACKET_LENGTH			8
279*4104d13fSDmitry Torokhov 
280*4104d13fSDmitry Torokhov 	/* We report in EV_MISC both the proximity and
281*4104d13fSDmitry Torokhov 	 * whether the report came from the stylus, tablet mouse
282*4104d13fSDmitry Torokhov 	 * or "unknown" -- Unknown when the tablet is in relative
283*4104d13fSDmitry Torokhov 	 * mode, because we only get report 1's.
284*4104d13fSDmitry Torokhov 	 */
285*4104d13fSDmitry Torokhov #define AIPTEK_REPORT_TOOL_UNKNOWN		0x10
286*4104d13fSDmitry Torokhov #define AIPTEK_REPORT_TOOL_STYLUS		0x20
287*4104d13fSDmitry Torokhov #define AIPTEK_REPORT_TOOL_MOUSE		0x40
288*4104d13fSDmitry Torokhov 
289*4104d13fSDmitry Torokhov static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
290*4104d13fSDmitry Torokhov static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
291*4104d13fSDmitry Torokhov 
292*4104d13fSDmitry Torokhov struct aiptek_features {
293*4104d13fSDmitry Torokhov 	int odmCode;		/* Tablet manufacturer code       */
294*4104d13fSDmitry Torokhov 	int modelCode;		/* Tablet model code (not unique) */
295*4104d13fSDmitry Torokhov 	int firmwareCode;	/* prom/eeprom version            */
296*4104d13fSDmitry Torokhov 	char usbPath[64 + 1];	/* device's physical usb path     */
297*4104d13fSDmitry Torokhov 	char inputPath[64 + 1];	/* input device path              */
298*4104d13fSDmitry Torokhov };
299*4104d13fSDmitry Torokhov 
300*4104d13fSDmitry Torokhov struct aiptek_settings {
301*4104d13fSDmitry Torokhov 	int pointerMode;	/* stylus-, mouse-only or either */
302*4104d13fSDmitry Torokhov 	int coordinateMode;	/* absolute/relative coords      */
303*4104d13fSDmitry Torokhov 	int toolMode;		/* pen, pencil, brush, etc. tool */
304*4104d13fSDmitry Torokhov 	int xTilt;		/* synthetic xTilt amount        */
305*4104d13fSDmitry Torokhov 	int yTilt;		/* synthetic yTilt amount        */
306*4104d13fSDmitry Torokhov 	int wheel;		/* synthetic wheel amount        */
307*4104d13fSDmitry Torokhov 	int stylusButtonUpper;	/* stylus upper btn delivers...  */
308*4104d13fSDmitry Torokhov 	int stylusButtonLower;	/* stylus lower btn delivers...  */
309*4104d13fSDmitry Torokhov 	int mouseButtonLeft;	/* mouse left btn delivers...    */
310*4104d13fSDmitry Torokhov 	int mouseButtonMiddle;	/* mouse middle btn delivers...  */
311*4104d13fSDmitry Torokhov 	int mouseButtonRight;	/* mouse right btn delivers...   */
312*4104d13fSDmitry Torokhov 	int programmableDelay;	/* delay for tablet programming  */
313*4104d13fSDmitry Torokhov 	int jitterDelay;	/* delay for hand jittering      */
314*4104d13fSDmitry Torokhov };
315*4104d13fSDmitry Torokhov 
316*4104d13fSDmitry Torokhov struct aiptek {
317*4104d13fSDmitry Torokhov 	struct input_dev *inputdev;		/* input device struct           */
318*4104d13fSDmitry Torokhov 	struct usb_device *usbdev;		/* usb device struct             */
319*4104d13fSDmitry Torokhov 	struct urb *urb;			/* urb for incoming reports      */
320*4104d13fSDmitry Torokhov 	dma_addr_t data_dma;			/* our dma stuffage              */
321*4104d13fSDmitry Torokhov 	struct aiptek_features features;	/* tablet's array of features    */
322*4104d13fSDmitry Torokhov 	struct aiptek_settings curSetting;	/* tablet's current programmable */
323*4104d13fSDmitry Torokhov 	struct aiptek_settings newSetting;	/* ... and new param settings    */
324*4104d13fSDmitry Torokhov 	unsigned int ifnum;			/* interface number for IO       */
325*4104d13fSDmitry Torokhov 	int diagnostic;				/* tablet diagnostic codes       */
326*4104d13fSDmitry Torokhov 	unsigned long eventCount;		/* event count                   */
327*4104d13fSDmitry Torokhov 	int inDelay;				/* jitter: in jitter delay?      */
328*4104d13fSDmitry Torokhov 	unsigned long endDelay;			/* jitter: time when delay ends  */
329*4104d13fSDmitry Torokhov 	int previousJitterable;			/* jitterable prev value     */
330*4104d13fSDmitry Torokhov 	unsigned char *data;			/* incoming packet data          */
331*4104d13fSDmitry Torokhov };
332*4104d13fSDmitry Torokhov 
333*4104d13fSDmitry Torokhov /*
334*4104d13fSDmitry Torokhov  * Permit easy lookup of keyboard events to send, versus
335*4104d13fSDmitry Torokhov  * the bitmap which comes from the tablet. This hides the
336*4104d13fSDmitry Torokhov  * issue that the F_keys are not sequentially numbered.
337*4104d13fSDmitry Torokhov  */
338*4104d13fSDmitry Torokhov static const int macroKeyEvents[] = {
339*4104d13fSDmitry Torokhov 	KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
340*4104d13fSDmitry Torokhov 	KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
341*4104d13fSDmitry Torokhov 	KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
342*4104d13fSDmitry Torokhov 	KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
343*4104d13fSDmitry Torokhov 	KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO,
344*4104d13fSDmitry Torokhov 	KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0
345*4104d13fSDmitry Torokhov };
346*4104d13fSDmitry Torokhov 
347*4104d13fSDmitry Torokhov /***********************************************************************
348*4104d13fSDmitry Torokhov  * Relative reports deliver values in 2's complement format to
349*4104d13fSDmitry Torokhov  * deal with negative offsets.
350*4104d13fSDmitry Torokhov  */
351*4104d13fSDmitry Torokhov static int aiptek_convert_from_2s_complement(unsigned char c)
352*4104d13fSDmitry Torokhov {
353*4104d13fSDmitry Torokhov 	int ret;
354*4104d13fSDmitry Torokhov 	unsigned char b = c;
355*4104d13fSDmitry Torokhov 	int negate = 0;
356*4104d13fSDmitry Torokhov 
357*4104d13fSDmitry Torokhov 	if ((b & 0x80) != 0) {
358*4104d13fSDmitry Torokhov 		b = ~b;
359*4104d13fSDmitry Torokhov 		b--;
360*4104d13fSDmitry Torokhov 		negate = 1;
361*4104d13fSDmitry Torokhov 	}
362*4104d13fSDmitry Torokhov 	ret = b;
363*4104d13fSDmitry Torokhov 	ret = (negate == 1) ? -ret : ret;
364*4104d13fSDmitry Torokhov 	return ret;
365*4104d13fSDmitry Torokhov }
366*4104d13fSDmitry Torokhov 
367*4104d13fSDmitry Torokhov /***********************************************************************
368*4104d13fSDmitry Torokhov  * aiptek_irq can receive one of six potential reports.
369*4104d13fSDmitry Torokhov  * The documentation for each is in the body of the function.
370*4104d13fSDmitry Torokhov  *
371*4104d13fSDmitry Torokhov  * The tablet reports on several attributes per invocation of
372*4104d13fSDmitry Torokhov  * aiptek_irq. Because the Linux Input Event system allows the
373*4104d13fSDmitry Torokhov  * transmission of ONE attribute per input_report_xxx() call,
374*4104d13fSDmitry Torokhov  * collation has to be done on the other end to reconstitute
375*4104d13fSDmitry Torokhov  * a complete tablet report. Further, the number of Input Event reports
376*4104d13fSDmitry Torokhov  * submitted varies, depending on what USB report type, and circumstance.
377*4104d13fSDmitry Torokhov  * To deal with this, EV_MSC is used to indicate an 'end-of-report'
378*4104d13fSDmitry Torokhov  * message. This has been an undocumented convention understood by the kernel
379*4104d13fSDmitry Torokhov  * tablet driver and clients such as gpm and XFree86's tablet drivers.
380*4104d13fSDmitry Torokhov  *
381*4104d13fSDmitry Torokhov  * Of the information received from the tablet, the one piece I
382*4104d13fSDmitry Torokhov  * cannot transmit is the proximity bit (without resorting to an EV_MSC
383*4104d13fSDmitry Torokhov  * convention above.) I therefore have taken over REL_MISC and ABS_MISC
384*4104d13fSDmitry Torokhov  * (for relative and absolute reports, respectively) for communicating
385*4104d13fSDmitry Torokhov  * Proximity. Why two events? I thought it interesting to know if the
386*4104d13fSDmitry Torokhov  * Proximity event occurred while the tablet was in absolute or relative
387*4104d13fSDmitry Torokhov  * mode.
388*4104d13fSDmitry Torokhov  *
389*4104d13fSDmitry Torokhov  * Other tablets use the notion of a certain minimum stylus pressure
390*4104d13fSDmitry Torokhov  * to infer proximity. While that could have been done, that is yet
391*4104d13fSDmitry Torokhov  * another 'by convention' behavior, the documentation for which
392*4104d13fSDmitry Torokhov  * would be spread between two (or more) pieces of software.
393*4104d13fSDmitry Torokhov  *
394*4104d13fSDmitry Torokhov  * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and
395*4104d13fSDmitry Torokhov  * replaced with the input_sync() method (which emits EV_SYN.)
396*4104d13fSDmitry Torokhov  */
397*4104d13fSDmitry Torokhov 
398*4104d13fSDmitry Torokhov static void aiptek_irq(struct urb *urb)
399*4104d13fSDmitry Torokhov {
400*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = urb->context;
401*4104d13fSDmitry Torokhov 	unsigned char *data = aiptek->data;
402*4104d13fSDmitry Torokhov 	struct input_dev *inputdev = aiptek->inputdev;
403*4104d13fSDmitry Torokhov 	int jitterable = 0;
404*4104d13fSDmitry Torokhov 	int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck;
405*4104d13fSDmitry Torokhov 
406*4104d13fSDmitry Torokhov 	switch (urb->status) {
407*4104d13fSDmitry Torokhov 	case 0:
408*4104d13fSDmitry Torokhov 		/* Success */
409*4104d13fSDmitry Torokhov 		break;
410*4104d13fSDmitry Torokhov 
411*4104d13fSDmitry Torokhov 	case -ECONNRESET:
412*4104d13fSDmitry Torokhov 	case -ENOENT:
413*4104d13fSDmitry Torokhov 	case -ESHUTDOWN:
414*4104d13fSDmitry Torokhov 		/* This urb is terminated, clean up */
415*4104d13fSDmitry Torokhov 		dbg("%s - urb shutting down with status: %d",
416*4104d13fSDmitry Torokhov 		    __FUNCTION__, urb->status);
417*4104d13fSDmitry Torokhov 		return;
418*4104d13fSDmitry Torokhov 
419*4104d13fSDmitry Torokhov 	default:
420*4104d13fSDmitry Torokhov 		dbg("%s - nonzero urb status received: %d",
421*4104d13fSDmitry Torokhov 		    __FUNCTION__, urb->status);
422*4104d13fSDmitry Torokhov 		goto exit;
423*4104d13fSDmitry Torokhov 	}
424*4104d13fSDmitry Torokhov 
425*4104d13fSDmitry Torokhov 	/* See if we are in a delay loop -- throw out report if true.
426*4104d13fSDmitry Torokhov 	 */
427*4104d13fSDmitry Torokhov 	if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) {
428*4104d13fSDmitry Torokhov 		goto exit;
429*4104d13fSDmitry Torokhov 	}
430*4104d13fSDmitry Torokhov 
431*4104d13fSDmitry Torokhov 	aiptek->inDelay = 0;
432*4104d13fSDmitry Torokhov 	aiptek->eventCount++;
433*4104d13fSDmitry Torokhov 
434*4104d13fSDmitry Torokhov 	/* Report 1 delivers relative coordinates with either a stylus
435*4104d13fSDmitry Torokhov 	 * or the mouse. You do not know, however, which input
436*4104d13fSDmitry Torokhov 	 * tool generated the event.
437*4104d13fSDmitry Torokhov 	 */
438*4104d13fSDmitry Torokhov 	if (data[0] == 1) {
439*4104d13fSDmitry Torokhov 		if (aiptek->curSetting.coordinateMode ==
440*4104d13fSDmitry Torokhov 		    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
441*4104d13fSDmitry Torokhov 			aiptek->diagnostic =
442*4104d13fSDmitry Torokhov 			    AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE;
443*4104d13fSDmitry Torokhov 		} else {
444*4104d13fSDmitry Torokhov 			x = aiptek_convert_from_2s_complement(data[2]);
445*4104d13fSDmitry Torokhov 			y = aiptek_convert_from_2s_complement(data[3]);
446*4104d13fSDmitry Torokhov 
447*4104d13fSDmitry Torokhov 			/* jitterable keeps track of whether any button has been pressed.
448*4104d13fSDmitry Torokhov 			 * We're also using it to remap the physical mouse button mask
449*4104d13fSDmitry Torokhov 			 * to pseudo-settings. (We don't specifically care about it's
450*4104d13fSDmitry Torokhov 			 * value after moving/transposing mouse button bitmasks, except
451*4104d13fSDmitry Torokhov 			 * that a non-zero value indicates that one or more
452*4104d13fSDmitry Torokhov 			 * mouse button was pressed.)
453*4104d13fSDmitry Torokhov 			 */
454*4104d13fSDmitry Torokhov 			jitterable = data[5] & 0x07;
455*4104d13fSDmitry Torokhov 
456*4104d13fSDmitry Torokhov 			left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
457*4104d13fSDmitry Torokhov 			right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
458*4104d13fSDmitry Torokhov 			middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
459*4104d13fSDmitry Torokhov 
460*4104d13fSDmitry Torokhov 			input_report_key(inputdev, BTN_LEFT, left);
461*4104d13fSDmitry Torokhov 			input_report_key(inputdev, BTN_MIDDLE, middle);
462*4104d13fSDmitry Torokhov 			input_report_key(inputdev, BTN_RIGHT, right);
463*4104d13fSDmitry Torokhov 			input_report_rel(inputdev, REL_X, x);
464*4104d13fSDmitry Torokhov 			input_report_rel(inputdev, REL_Y, y);
465*4104d13fSDmitry Torokhov 			input_report_rel(inputdev, REL_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
466*4104d13fSDmitry Torokhov 
467*4104d13fSDmitry Torokhov 			/* Wheel support is in the form of a single-event
468*4104d13fSDmitry Torokhov 			 * firing.
469*4104d13fSDmitry Torokhov 			 */
470*4104d13fSDmitry Torokhov 			if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
471*4104d13fSDmitry Torokhov 				input_report_rel(inputdev, REL_WHEEL,
472*4104d13fSDmitry Torokhov 						 aiptek->curSetting.wheel);
473*4104d13fSDmitry Torokhov 				aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
474*4104d13fSDmitry Torokhov 			}
475*4104d13fSDmitry Torokhov 			input_sync(inputdev);
476*4104d13fSDmitry Torokhov 		}
477*4104d13fSDmitry Torokhov 	}
478*4104d13fSDmitry Torokhov 	/* Report 2 is delivered only by the stylus, and delivers
479*4104d13fSDmitry Torokhov 	 * absolute coordinates.
480*4104d13fSDmitry Torokhov 	 */
481*4104d13fSDmitry Torokhov 	else if (data[0] == 2) {
482*4104d13fSDmitry Torokhov 		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
483*4104d13fSDmitry Torokhov 			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
484*4104d13fSDmitry Torokhov 		} else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE
485*4104d13fSDmitry Torokhov 			    (aiptek->curSetting.pointerMode)) {
486*4104d13fSDmitry Torokhov 				aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
487*4104d13fSDmitry Torokhov 		} else {
488*4104d13fSDmitry Torokhov 			x = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
489*4104d13fSDmitry Torokhov 			y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
490*4104d13fSDmitry Torokhov 			z = le16_to_cpu(get_unaligned((__le16 *) (data + 6)));
491*4104d13fSDmitry Torokhov 
492*4104d13fSDmitry Torokhov 			p = (data[5] & 0x01) != 0 ? 1 : 0;
493*4104d13fSDmitry Torokhov 			dv = (data[5] & 0x02) != 0 ? 1 : 0;
494*4104d13fSDmitry Torokhov 			tip = (data[5] & 0x04) != 0 ? 1 : 0;
495*4104d13fSDmitry Torokhov 
496*4104d13fSDmitry Torokhov 			/* Use jitterable to re-arrange button masks
497*4104d13fSDmitry Torokhov 			 */
498*4104d13fSDmitry Torokhov 			jitterable = data[5] & 0x18;
499*4104d13fSDmitry Torokhov 
500*4104d13fSDmitry Torokhov 			bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
501*4104d13fSDmitry Torokhov 			pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
502*4104d13fSDmitry Torokhov 
503*4104d13fSDmitry Torokhov 			/* dv indicates 'data valid' (e.g., the tablet is in sync
504*4104d13fSDmitry Torokhov 			 * and has delivered a "correct" report) We will ignore
505*4104d13fSDmitry Torokhov 			 * all 'bad' reports...
506*4104d13fSDmitry Torokhov 			 */
507*4104d13fSDmitry Torokhov 			if (dv != 0) {
508*4104d13fSDmitry Torokhov 				/* If we've not already sent a tool_button_?? code, do
509*4104d13fSDmitry Torokhov 				 * so now. Then set FIRED_BIT so it won't be resent unless
510*4104d13fSDmitry Torokhov 				 * the user forces FIRED_BIT off.
511*4104d13fSDmitry Torokhov 				 */
512*4104d13fSDmitry Torokhov 				if (TOOL_BUTTON_FIRED
513*4104d13fSDmitry Torokhov 				    (aiptek->curSetting.toolMode) == 0) {
514*4104d13fSDmitry Torokhov 					input_report_key(inputdev,
515*4104d13fSDmitry Torokhov 							 TOOL_BUTTON(aiptek->curSetting.toolMode),
516*4104d13fSDmitry Torokhov 							 1);
517*4104d13fSDmitry Torokhov 					aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
518*4104d13fSDmitry Torokhov 				}
519*4104d13fSDmitry Torokhov 
520*4104d13fSDmitry Torokhov 				if (p != 0) {
521*4104d13fSDmitry Torokhov 					input_report_abs(inputdev, ABS_X, x);
522*4104d13fSDmitry Torokhov 					input_report_abs(inputdev, ABS_Y, y);
523*4104d13fSDmitry Torokhov 					input_report_abs(inputdev, ABS_PRESSURE, z);
524*4104d13fSDmitry Torokhov 
525*4104d13fSDmitry Torokhov 					input_report_key(inputdev, BTN_TOUCH, tip);
526*4104d13fSDmitry Torokhov 					input_report_key(inputdev, BTN_STYLUS, bs);
527*4104d13fSDmitry Torokhov 					input_report_key(inputdev, BTN_STYLUS2, pck);
528*4104d13fSDmitry Torokhov 
529*4104d13fSDmitry Torokhov 					if (aiptek->curSetting.xTilt !=
530*4104d13fSDmitry Torokhov 					    AIPTEK_TILT_DISABLE) {
531*4104d13fSDmitry Torokhov 						input_report_abs(inputdev,
532*4104d13fSDmitry Torokhov 								 ABS_TILT_X,
533*4104d13fSDmitry Torokhov 								 aiptek->curSetting.xTilt);
534*4104d13fSDmitry Torokhov 					}
535*4104d13fSDmitry Torokhov 					if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) {
536*4104d13fSDmitry Torokhov 						input_report_abs(inputdev,
537*4104d13fSDmitry Torokhov 								 ABS_TILT_Y,
538*4104d13fSDmitry Torokhov 								 aiptek->curSetting.yTilt);
539*4104d13fSDmitry Torokhov 					}
540*4104d13fSDmitry Torokhov 
541*4104d13fSDmitry Torokhov 					/* Wheel support is in the form of a single-event
542*4104d13fSDmitry Torokhov 					 * firing.
543*4104d13fSDmitry Torokhov 					 */
544*4104d13fSDmitry Torokhov 					if (aiptek->curSetting.wheel !=
545*4104d13fSDmitry Torokhov 					    AIPTEK_WHEEL_DISABLE) {
546*4104d13fSDmitry Torokhov 						input_report_abs(inputdev,
547*4104d13fSDmitry Torokhov 								 ABS_WHEEL,
548*4104d13fSDmitry Torokhov 								 aiptek->curSetting.wheel);
549*4104d13fSDmitry Torokhov 						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
550*4104d13fSDmitry Torokhov 					}
551*4104d13fSDmitry Torokhov 				}
552*4104d13fSDmitry Torokhov 				input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS);
553*4104d13fSDmitry Torokhov 				input_sync(inputdev);
554*4104d13fSDmitry Torokhov 			}
555*4104d13fSDmitry Torokhov 		}
556*4104d13fSDmitry Torokhov 	}
557*4104d13fSDmitry Torokhov 	/* Report 3's come from the mouse in absolute mode.
558*4104d13fSDmitry Torokhov 	 */
559*4104d13fSDmitry Torokhov 	else if (data[0] == 3) {
560*4104d13fSDmitry Torokhov 		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
561*4104d13fSDmitry Torokhov 			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
562*4104d13fSDmitry Torokhov 		} else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE
563*4104d13fSDmitry Torokhov 			(aiptek->curSetting.pointerMode)) {
564*4104d13fSDmitry Torokhov 			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
565*4104d13fSDmitry Torokhov 		} else {
566*4104d13fSDmitry Torokhov 			x = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
567*4104d13fSDmitry Torokhov 			y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
568*4104d13fSDmitry Torokhov 
569*4104d13fSDmitry Torokhov 			jitterable = data[5] & 0x1c;
570*4104d13fSDmitry Torokhov 
571*4104d13fSDmitry Torokhov 			p = (data[5] & 0x01) != 0 ? 1 : 0;
572*4104d13fSDmitry Torokhov 			dv = (data[5] & 0x02) != 0 ? 1 : 0;
573*4104d13fSDmitry Torokhov 			left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
574*4104d13fSDmitry Torokhov 			right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
575*4104d13fSDmitry Torokhov 			middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
576*4104d13fSDmitry Torokhov 
577*4104d13fSDmitry Torokhov 			if (dv != 0) {
578*4104d13fSDmitry Torokhov 				/* If we've not already sent a tool_button_?? code, do
579*4104d13fSDmitry Torokhov 				 * so now. Then set FIRED_BIT so it won't be resent unless
580*4104d13fSDmitry Torokhov 				 * the user forces FIRED_BIT off.
581*4104d13fSDmitry Torokhov 				 */
582*4104d13fSDmitry Torokhov 				if (TOOL_BUTTON_FIRED
583*4104d13fSDmitry Torokhov 				    (aiptek->curSetting.toolMode) == 0) {
584*4104d13fSDmitry Torokhov 					input_report_key(inputdev,
585*4104d13fSDmitry Torokhov 							 TOOL_BUTTON(aiptek->curSetting.toolMode),
586*4104d13fSDmitry Torokhov 							 1);
587*4104d13fSDmitry Torokhov 					aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
588*4104d13fSDmitry Torokhov 				}
589*4104d13fSDmitry Torokhov 
590*4104d13fSDmitry Torokhov 				if (p != 0) {
591*4104d13fSDmitry Torokhov 					input_report_abs(inputdev, ABS_X, x);
592*4104d13fSDmitry Torokhov 					input_report_abs(inputdev, ABS_Y, y);
593*4104d13fSDmitry Torokhov 
594*4104d13fSDmitry Torokhov 					input_report_key(inputdev, BTN_LEFT, left);
595*4104d13fSDmitry Torokhov 					input_report_key(inputdev, BTN_MIDDLE, middle);
596*4104d13fSDmitry Torokhov 					input_report_key(inputdev, BTN_RIGHT, right);
597*4104d13fSDmitry Torokhov 
598*4104d13fSDmitry Torokhov 					/* Wheel support is in the form of a single-event
599*4104d13fSDmitry Torokhov 					 * firing.
600*4104d13fSDmitry Torokhov 					 */
601*4104d13fSDmitry Torokhov 					if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
602*4104d13fSDmitry Torokhov 						input_report_abs(inputdev,
603*4104d13fSDmitry Torokhov 								 ABS_WHEEL,
604*4104d13fSDmitry Torokhov 								 aiptek->curSetting.wheel);
605*4104d13fSDmitry Torokhov 						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
606*4104d13fSDmitry Torokhov 					}
607*4104d13fSDmitry Torokhov 				}
608*4104d13fSDmitry Torokhov 				input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
609*4104d13fSDmitry Torokhov 				input_sync(inputdev);
610*4104d13fSDmitry Torokhov 			}
611*4104d13fSDmitry Torokhov 		}
612*4104d13fSDmitry Torokhov 	}
613*4104d13fSDmitry Torokhov 	/* Report 4s come from the macro keys when pressed by stylus
614*4104d13fSDmitry Torokhov 	 */
615*4104d13fSDmitry Torokhov 	else if (data[0] == 4) {
616*4104d13fSDmitry Torokhov 		jitterable = data[1] & 0x18;
617*4104d13fSDmitry Torokhov 
618*4104d13fSDmitry Torokhov 		p = (data[1] & 0x01) != 0 ? 1 : 0;
619*4104d13fSDmitry Torokhov 		dv = (data[1] & 0x02) != 0 ? 1 : 0;
620*4104d13fSDmitry Torokhov 		tip = (data[1] & 0x04) != 0 ? 1 : 0;
621*4104d13fSDmitry Torokhov 		bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
622*4104d13fSDmitry Torokhov 		pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
623*4104d13fSDmitry Torokhov 
624*4104d13fSDmitry Torokhov 		macro = data[3];
625*4104d13fSDmitry Torokhov 		z = le16_to_cpu(get_unaligned((__le16 *) (data + 4)));
626*4104d13fSDmitry Torokhov 
627*4104d13fSDmitry Torokhov 		if (dv != 0) {
628*4104d13fSDmitry Torokhov 			/* If we've not already sent a tool_button_?? code, do
629*4104d13fSDmitry Torokhov 			 * so now. Then set FIRED_BIT so it won't be resent unless
630*4104d13fSDmitry Torokhov 			 * the user forces FIRED_BIT off.
631*4104d13fSDmitry Torokhov 			 */
632*4104d13fSDmitry Torokhov 			if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
633*4104d13fSDmitry Torokhov 				input_report_key(inputdev,
634*4104d13fSDmitry Torokhov 						 TOOL_BUTTON(aiptek->curSetting.toolMode),
635*4104d13fSDmitry Torokhov 						 1);
636*4104d13fSDmitry Torokhov 				aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
637*4104d13fSDmitry Torokhov 			}
638*4104d13fSDmitry Torokhov 
639*4104d13fSDmitry Torokhov 			if (p != 0) {
640*4104d13fSDmitry Torokhov 				input_report_key(inputdev, BTN_TOUCH, tip);
641*4104d13fSDmitry Torokhov 				input_report_key(inputdev, BTN_STYLUS, bs);
642*4104d13fSDmitry Torokhov 				input_report_key(inputdev, BTN_STYLUS2, pck);
643*4104d13fSDmitry Torokhov 				input_report_abs(inputdev, ABS_PRESSURE, z);
644*4104d13fSDmitry Torokhov 			}
645*4104d13fSDmitry Torokhov 
646*4104d13fSDmitry Torokhov 			/* For safety, we're sending key 'break' codes for the
647*4104d13fSDmitry Torokhov 			 * neighboring macro keys.
648*4104d13fSDmitry Torokhov 			 */
649*4104d13fSDmitry Torokhov 			if (macro > 0) {
650*4104d13fSDmitry Torokhov 				input_report_key(inputdev,
651*4104d13fSDmitry Torokhov 						 macroKeyEvents[macro - 1], 0);
652*4104d13fSDmitry Torokhov 			}
653*4104d13fSDmitry Torokhov 			if (macro < 25) {
654*4104d13fSDmitry Torokhov 				input_report_key(inputdev,
655*4104d13fSDmitry Torokhov 						 macroKeyEvents[macro + 1], 0);
656*4104d13fSDmitry Torokhov 			}
657*4104d13fSDmitry Torokhov 			input_report_key(inputdev, macroKeyEvents[macro], p);
658*4104d13fSDmitry Torokhov 			input_report_abs(inputdev, ABS_MISC,
659*4104d13fSDmitry Torokhov 					 p | AIPTEK_REPORT_TOOL_STYLUS);
660*4104d13fSDmitry Torokhov 			input_sync(inputdev);
661*4104d13fSDmitry Torokhov 		}
662*4104d13fSDmitry Torokhov 	}
663*4104d13fSDmitry Torokhov 	/* Report 5s come from the macro keys when pressed by mouse
664*4104d13fSDmitry Torokhov 	 */
665*4104d13fSDmitry Torokhov 	else if (data[0] == 5) {
666*4104d13fSDmitry Torokhov 		jitterable = data[1] & 0x1c;
667*4104d13fSDmitry Torokhov 
668*4104d13fSDmitry Torokhov 		p = (data[1] & 0x01) != 0 ? 1 : 0;
669*4104d13fSDmitry Torokhov 		dv = (data[1] & 0x02) != 0 ? 1 : 0;
670*4104d13fSDmitry Torokhov 		left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
671*4104d13fSDmitry Torokhov 		right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
672*4104d13fSDmitry Torokhov 		middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
673*4104d13fSDmitry Torokhov 		macro = data[3];
674*4104d13fSDmitry Torokhov 
675*4104d13fSDmitry Torokhov 		if (dv != 0) {
676*4104d13fSDmitry Torokhov 			/* If we've not already sent a tool_button_?? code, do
677*4104d13fSDmitry Torokhov 			 * so now. Then set FIRED_BIT so it won't be resent unless
678*4104d13fSDmitry Torokhov 			 * the user forces FIRED_BIT off.
679*4104d13fSDmitry Torokhov 			 */
680*4104d13fSDmitry Torokhov 			if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
681*4104d13fSDmitry Torokhov 				input_report_key(inputdev,
682*4104d13fSDmitry Torokhov 						 TOOL_BUTTON(aiptek->curSetting.toolMode),
683*4104d13fSDmitry Torokhov 						 1);
684*4104d13fSDmitry Torokhov 				aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
685*4104d13fSDmitry Torokhov 			}
686*4104d13fSDmitry Torokhov 
687*4104d13fSDmitry Torokhov 			if (p != 0) {
688*4104d13fSDmitry Torokhov 				input_report_key(inputdev, BTN_LEFT, left);
689*4104d13fSDmitry Torokhov 				input_report_key(inputdev, BTN_MIDDLE, middle);
690*4104d13fSDmitry Torokhov 				input_report_key(inputdev, BTN_RIGHT, right);
691*4104d13fSDmitry Torokhov 			}
692*4104d13fSDmitry Torokhov 
693*4104d13fSDmitry Torokhov 			/* For safety, we're sending key 'break' codes for the
694*4104d13fSDmitry Torokhov 			 * neighboring macro keys.
695*4104d13fSDmitry Torokhov 			 */
696*4104d13fSDmitry Torokhov 			if (macro > 0) {
697*4104d13fSDmitry Torokhov 				input_report_key(inputdev,
698*4104d13fSDmitry Torokhov 						 macroKeyEvents[macro - 1], 0);
699*4104d13fSDmitry Torokhov 			}
700*4104d13fSDmitry Torokhov 			if (macro < 25) {
701*4104d13fSDmitry Torokhov 				input_report_key(inputdev,
702*4104d13fSDmitry Torokhov 						 macroKeyEvents[macro + 1], 0);
703*4104d13fSDmitry Torokhov 			}
704*4104d13fSDmitry Torokhov 
705*4104d13fSDmitry Torokhov 			input_report_key(inputdev, macroKeyEvents[macro], 1);
706*4104d13fSDmitry Torokhov 			input_report_rel(inputdev, ABS_MISC,
707*4104d13fSDmitry Torokhov 					 p | AIPTEK_REPORT_TOOL_MOUSE);
708*4104d13fSDmitry Torokhov 			input_sync(inputdev);
709*4104d13fSDmitry Torokhov 		}
710*4104d13fSDmitry Torokhov 	}
711*4104d13fSDmitry Torokhov 	/* We have no idea which tool can generate a report 6. Theoretically,
712*4104d13fSDmitry Torokhov 	 * neither need to, having been given reports 4 & 5 for such use.
713*4104d13fSDmitry Torokhov 	 * However, report 6 is the 'official-looking' report for macroKeys;
714*4104d13fSDmitry Torokhov 	 * reports 4 & 5 supposively are used to support unnamed, unknown
715*4104d13fSDmitry Torokhov 	 * hat switches (which just so happen to be the macroKeys.)
716*4104d13fSDmitry Torokhov 	 */
717*4104d13fSDmitry Torokhov 	else if (data[0] == 6) {
718*4104d13fSDmitry Torokhov 		macro = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
719*4104d13fSDmitry Torokhov 		if (macro > 0) {
720*4104d13fSDmitry Torokhov 			input_report_key(inputdev, macroKeyEvents[macro - 1],
721*4104d13fSDmitry Torokhov 					 0);
722*4104d13fSDmitry Torokhov 		}
723*4104d13fSDmitry Torokhov 		if (macro < 25) {
724*4104d13fSDmitry Torokhov 			input_report_key(inputdev, macroKeyEvents[macro + 1],
725*4104d13fSDmitry Torokhov 					 0);
726*4104d13fSDmitry Torokhov 		}
727*4104d13fSDmitry Torokhov 
728*4104d13fSDmitry Torokhov 		/* If we've not already sent a tool_button_?? code, do
729*4104d13fSDmitry Torokhov 		 * so now. Then set FIRED_BIT so it won't be resent unless
730*4104d13fSDmitry Torokhov 		 * the user forces FIRED_BIT off.
731*4104d13fSDmitry Torokhov 		 */
732*4104d13fSDmitry Torokhov 		if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
733*4104d13fSDmitry Torokhov 			input_report_key(inputdev,
734*4104d13fSDmitry Torokhov 					 TOOL_BUTTON(aiptek->curSetting.
735*4104d13fSDmitry Torokhov 						     toolMode), 1);
736*4104d13fSDmitry Torokhov 			aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
737*4104d13fSDmitry Torokhov 		}
738*4104d13fSDmitry Torokhov 
739*4104d13fSDmitry Torokhov 		input_report_key(inputdev, macroKeyEvents[macro], 1);
740*4104d13fSDmitry Torokhov 		input_report_abs(inputdev, ABS_MISC,
741*4104d13fSDmitry Torokhov 				 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
742*4104d13fSDmitry Torokhov 		input_sync(inputdev);
743*4104d13fSDmitry Torokhov 	} else {
744*4104d13fSDmitry Torokhov 		dbg("Unknown report %d", data[0]);
745*4104d13fSDmitry Torokhov 	}
746*4104d13fSDmitry Torokhov 
747*4104d13fSDmitry Torokhov 	/* Jitter may occur when the user presses a button on the stlyus
748*4104d13fSDmitry Torokhov 	 * or the mouse. What we do to prevent that is wait 'x' milliseconds
749*4104d13fSDmitry Torokhov 	 * following a 'jitterable' event, which should give the hand some time
750*4104d13fSDmitry Torokhov 	 * stabilize itself.
751*4104d13fSDmitry Torokhov 	 *
752*4104d13fSDmitry Torokhov 	 * We just introduced aiptek->previousJitterable to carry forth the
753*4104d13fSDmitry Torokhov 	 * notion that jitter occurs when the button state changes from on to off:
754*4104d13fSDmitry Torokhov 	 * a person drawing, holding a button down is not subject to jittering.
755*4104d13fSDmitry Torokhov 	 * With that in mind, changing from upper button depressed to lower button
756*4104d13fSDmitry Torokhov 	 * WILL transition through a jitter delay.
757*4104d13fSDmitry Torokhov 	 */
758*4104d13fSDmitry Torokhov 
759*4104d13fSDmitry Torokhov 	if (aiptek->previousJitterable != jitterable &&
760*4104d13fSDmitry Torokhov 	    aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) {
761*4104d13fSDmitry Torokhov 		aiptek->endDelay = jiffies +
762*4104d13fSDmitry Torokhov 		    ((aiptek->curSetting.jitterDelay * HZ) / 1000);
763*4104d13fSDmitry Torokhov 		aiptek->inDelay = 1;
764*4104d13fSDmitry Torokhov 	}
765*4104d13fSDmitry Torokhov 	aiptek->previousJitterable = jitterable;
766*4104d13fSDmitry Torokhov 
767*4104d13fSDmitry Torokhov exit:
768*4104d13fSDmitry Torokhov 	retval = usb_submit_urb(urb, GFP_ATOMIC);
769*4104d13fSDmitry Torokhov 	if (retval != 0) {
770*4104d13fSDmitry Torokhov 		err("%s - usb_submit_urb failed with result %d",
771*4104d13fSDmitry Torokhov 		    __FUNCTION__, retval);
772*4104d13fSDmitry Torokhov 	}
773*4104d13fSDmitry Torokhov }
774*4104d13fSDmitry Torokhov 
775*4104d13fSDmitry Torokhov /***********************************************************************
776*4104d13fSDmitry Torokhov  * These are the USB id's known so far. We do not identify them to
777*4104d13fSDmitry Torokhov  * specific Aiptek model numbers, because there has been overlaps,
778*4104d13fSDmitry Torokhov  * use, and reuse of id's in existing models. Certain models have
779*4104d13fSDmitry Torokhov  * been known to use more than one ID, indicative perhaps of
780*4104d13fSDmitry Torokhov  * manufacturing revisions. In any event, we consider these
781*4104d13fSDmitry Torokhov  * IDs to not be model-specific nor unique.
782*4104d13fSDmitry Torokhov  */
783*4104d13fSDmitry Torokhov static const struct usb_device_id aiptek_ids[] = {
784*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)},
785*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)},
786*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)},
787*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)},
788*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)},
789*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)},
790*4104d13fSDmitry Torokhov 	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)},
791*4104d13fSDmitry Torokhov 	{}
792*4104d13fSDmitry Torokhov };
793*4104d13fSDmitry Torokhov 
794*4104d13fSDmitry Torokhov MODULE_DEVICE_TABLE(usb, aiptek_ids);
795*4104d13fSDmitry Torokhov 
796*4104d13fSDmitry Torokhov /***********************************************************************
797*4104d13fSDmitry Torokhov  * Open an instance of the tablet driver.
798*4104d13fSDmitry Torokhov  */
799*4104d13fSDmitry Torokhov static int aiptek_open(struct input_dev *inputdev)
800*4104d13fSDmitry Torokhov {
801*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = input_get_drvdata(inputdev);
802*4104d13fSDmitry Torokhov 
803*4104d13fSDmitry Torokhov 	aiptek->urb->dev = aiptek->usbdev;
804*4104d13fSDmitry Torokhov 	if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0)
805*4104d13fSDmitry Torokhov 		return -EIO;
806*4104d13fSDmitry Torokhov 
807*4104d13fSDmitry Torokhov 	return 0;
808*4104d13fSDmitry Torokhov }
809*4104d13fSDmitry Torokhov 
810*4104d13fSDmitry Torokhov /***********************************************************************
811*4104d13fSDmitry Torokhov  * Close an instance of the tablet driver.
812*4104d13fSDmitry Torokhov  */
813*4104d13fSDmitry Torokhov static void aiptek_close(struct input_dev *inputdev)
814*4104d13fSDmitry Torokhov {
815*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = input_get_drvdata(inputdev);
816*4104d13fSDmitry Torokhov 
817*4104d13fSDmitry Torokhov 	usb_kill_urb(aiptek->urb);
818*4104d13fSDmitry Torokhov }
819*4104d13fSDmitry Torokhov 
820*4104d13fSDmitry Torokhov /***********************************************************************
821*4104d13fSDmitry Torokhov  * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x,
822*4104d13fSDmitry Torokhov  * where they were known as usb_set_report and usb_get_report.
823*4104d13fSDmitry Torokhov  */
824*4104d13fSDmitry Torokhov static int
825*4104d13fSDmitry Torokhov aiptek_set_report(struct aiptek *aiptek,
826*4104d13fSDmitry Torokhov 		  unsigned char report_type,
827*4104d13fSDmitry Torokhov 		  unsigned char report_id, void *buffer, int size)
828*4104d13fSDmitry Torokhov {
829*4104d13fSDmitry Torokhov 	return usb_control_msg(aiptek->usbdev,
830*4104d13fSDmitry Torokhov 			       usb_sndctrlpipe(aiptek->usbdev, 0),
831*4104d13fSDmitry Torokhov 			       USB_REQ_SET_REPORT,
832*4104d13fSDmitry Torokhov 			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
833*4104d13fSDmitry Torokhov 			       USB_DIR_OUT, (report_type << 8) + report_id,
834*4104d13fSDmitry Torokhov 			       aiptek->ifnum, buffer, size, 5000);
835*4104d13fSDmitry Torokhov }
836*4104d13fSDmitry Torokhov 
837*4104d13fSDmitry Torokhov static int
838*4104d13fSDmitry Torokhov aiptek_get_report(struct aiptek *aiptek,
839*4104d13fSDmitry Torokhov 		  unsigned char report_type,
840*4104d13fSDmitry Torokhov 		  unsigned char report_id, void *buffer, int size)
841*4104d13fSDmitry Torokhov {
842*4104d13fSDmitry Torokhov 	return usb_control_msg(aiptek->usbdev,
843*4104d13fSDmitry Torokhov 			       usb_rcvctrlpipe(aiptek->usbdev, 0),
844*4104d13fSDmitry Torokhov 			       USB_REQ_GET_REPORT,
845*4104d13fSDmitry Torokhov 			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
846*4104d13fSDmitry Torokhov 			       USB_DIR_IN, (report_type << 8) + report_id,
847*4104d13fSDmitry Torokhov 			       aiptek->ifnum, buffer, size, 5000);
848*4104d13fSDmitry Torokhov }
849*4104d13fSDmitry Torokhov 
850*4104d13fSDmitry Torokhov /***********************************************************************
851*4104d13fSDmitry Torokhov  * Send a command to the tablet.
852*4104d13fSDmitry Torokhov  */
853*4104d13fSDmitry Torokhov static int
854*4104d13fSDmitry Torokhov aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data)
855*4104d13fSDmitry Torokhov {
856*4104d13fSDmitry Torokhov 	const int sizeof_buf = 3 * sizeof(u8);
857*4104d13fSDmitry Torokhov 	int ret;
858*4104d13fSDmitry Torokhov 	u8 *buf;
859*4104d13fSDmitry Torokhov 
860*4104d13fSDmitry Torokhov 	buf = kmalloc(sizeof_buf, GFP_KERNEL);
861*4104d13fSDmitry Torokhov 	if (!buf)
862*4104d13fSDmitry Torokhov 		return -ENOMEM;
863*4104d13fSDmitry Torokhov 
864*4104d13fSDmitry Torokhov 	buf[0] = 2;
865*4104d13fSDmitry Torokhov 	buf[1] = command;
866*4104d13fSDmitry Torokhov 	buf[2] = data;
867*4104d13fSDmitry Torokhov 
868*4104d13fSDmitry Torokhov 	if ((ret =
869*4104d13fSDmitry Torokhov 	     aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
870*4104d13fSDmitry Torokhov 		dbg("aiptek_program: failed, tried to send: 0x%02x 0x%02x",
871*4104d13fSDmitry Torokhov 		    command, data);
872*4104d13fSDmitry Torokhov 	}
873*4104d13fSDmitry Torokhov 	kfree(buf);
874*4104d13fSDmitry Torokhov 	return ret < 0 ? ret : 0;
875*4104d13fSDmitry Torokhov }
876*4104d13fSDmitry Torokhov 
877*4104d13fSDmitry Torokhov /***********************************************************************
878*4104d13fSDmitry Torokhov  * Retrieve information from the tablet. Querying info is defined as first
879*4104d13fSDmitry Torokhov  * sending the {command,data} sequence as a command, followed by a wait
880*4104d13fSDmitry Torokhov  * (aka, "programmaticDelay") and then a "read" request.
881*4104d13fSDmitry Torokhov  */
882*4104d13fSDmitry Torokhov static int
883*4104d13fSDmitry Torokhov aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
884*4104d13fSDmitry Torokhov {
885*4104d13fSDmitry Torokhov 	const int sizeof_buf = 3 * sizeof(u8);
886*4104d13fSDmitry Torokhov 	int ret;
887*4104d13fSDmitry Torokhov 	u8 *buf;
888*4104d13fSDmitry Torokhov 
889*4104d13fSDmitry Torokhov 	buf = kmalloc(sizeof_buf, GFP_KERNEL);
890*4104d13fSDmitry Torokhov 	if (!buf)
891*4104d13fSDmitry Torokhov 		return -ENOMEM;
892*4104d13fSDmitry Torokhov 
893*4104d13fSDmitry Torokhov 	buf[0] = 2;
894*4104d13fSDmitry Torokhov 	buf[1] = command;
895*4104d13fSDmitry Torokhov 	buf[2] = data;
896*4104d13fSDmitry Torokhov 
897*4104d13fSDmitry Torokhov 	if (aiptek_command(aiptek, command, data) != 0) {
898*4104d13fSDmitry Torokhov 		kfree(buf);
899*4104d13fSDmitry Torokhov 		return -EIO;
900*4104d13fSDmitry Torokhov 	}
901*4104d13fSDmitry Torokhov 	msleep(aiptek->curSetting.programmableDelay);
902*4104d13fSDmitry Torokhov 
903*4104d13fSDmitry Torokhov 	if ((ret =
904*4104d13fSDmitry Torokhov 	     aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
905*4104d13fSDmitry Torokhov 		dbg("aiptek_query failed: returned 0x%02x 0x%02x 0x%02x",
906*4104d13fSDmitry Torokhov 		    buf[0], buf[1], buf[2]);
907*4104d13fSDmitry Torokhov 		ret = -EIO;
908*4104d13fSDmitry Torokhov 	} else {
909*4104d13fSDmitry Torokhov 		ret = le16_to_cpu(get_unaligned((__le16 *) (buf + 1)));
910*4104d13fSDmitry Torokhov 	}
911*4104d13fSDmitry Torokhov 	kfree(buf);
912*4104d13fSDmitry Torokhov 	return ret;
913*4104d13fSDmitry Torokhov }
914*4104d13fSDmitry Torokhov 
915*4104d13fSDmitry Torokhov /***********************************************************************
916*4104d13fSDmitry Torokhov  * Program the tablet into either absolute or relative mode.
917*4104d13fSDmitry Torokhov  * We also get information about the tablet's size.
918*4104d13fSDmitry Torokhov  */
919*4104d13fSDmitry Torokhov static int aiptek_program_tablet(struct aiptek *aiptek)
920*4104d13fSDmitry Torokhov {
921*4104d13fSDmitry Torokhov 	int ret;
922*4104d13fSDmitry Torokhov 	/* Execute Resolution500LPI */
923*4104d13fSDmitry Torokhov 	if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0)
924*4104d13fSDmitry Torokhov 		return ret;
925*4104d13fSDmitry Torokhov 
926*4104d13fSDmitry Torokhov 	/* Query getModelCode */
927*4104d13fSDmitry Torokhov 	if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0)
928*4104d13fSDmitry Torokhov 		return ret;
929*4104d13fSDmitry Torokhov 	aiptek->features.modelCode = ret & 0xff;
930*4104d13fSDmitry Torokhov 
931*4104d13fSDmitry Torokhov 	/* Query getODMCode */
932*4104d13fSDmitry Torokhov 	if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0)
933*4104d13fSDmitry Torokhov 		return ret;
934*4104d13fSDmitry Torokhov 	aiptek->features.odmCode = ret;
935*4104d13fSDmitry Torokhov 
936*4104d13fSDmitry Torokhov 	/* Query getFirmwareCode */
937*4104d13fSDmitry Torokhov 	if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0)
938*4104d13fSDmitry Torokhov 		return ret;
939*4104d13fSDmitry Torokhov 	aiptek->features.firmwareCode = ret;
940*4104d13fSDmitry Torokhov 
941*4104d13fSDmitry Torokhov 	/* Query getXextension */
942*4104d13fSDmitry Torokhov 	if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
943*4104d13fSDmitry Torokhov 		return ret;
944*4104d13fSDmitry Torokhov 	aiptek->inputdev->absmin[ABS_X] = 0;
945*4104d13fSDmitry Torokhov 	aiptek->inputdev->absmax[ABS_X] = ret - 1;
946*4104d13fSDmitry Torokhov 
947*4104d13fSDmitry Torokhov 	/* Query getYextension */
948*4104d13fSDmitry Torokhov 	if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
949*4104d13fSDmitry Torokhov 		return ret;
950*4104d13fSDmitry Torokhov 	aiptek->inputdev->absmin[ABS_Y] = 0;
951*4104d13fSDmitry Torokhov 	aiptek->inputdev->absmax[ABS_Y] = ret - 1;
952*4104d13fSDmitry Torokhov 
953*4104d13fSDmitry Torokhov 	/* Query getPressureLevels */
954*4104d13fSDmitry Torokhov 	if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
955*4104d13fSDmitry Torokhov 		return ret;
956*4104d13fSDmitry Torokhov 	aiptek->inputdev->absmin[ABS_PRESSURE] = 0;
957*4104d13fSDmitry Torokhov 	aiptek->inputdev->absmax[ABS_PRESSURE] = ret - 1;
958*4104d13fSDmitry Torokhov 
959*4104d13fSDmitry Torokhov 	/* Depending on whether we are in absolute or relative mode, we will
960*4104d13fSDmitry Torokhov 	 * do a switchToTablet(absolute) or switchToMouse(relative) command.
961*4104d13fSDmitry Torokhov 	 */
962*4104d13fSDmitry Torokhov 	if (aiptek->curSetting.coordinateMode ==
963*4104d13fSDmitry Torokhov 	    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
964*4104d13fSDmitry Torokhov 		/* Execute switchToTablet */
965*4104d13fSDmitry Torokhov 		if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) {
966*4104d13fSDmitry Torokhov 			return ret;
967*4104d13fSDmitry Torokhov 		}
968*4104d13fSDmitry Torokhov 	} else {
969*4104d13fSDmitry Torokhov 		/* Execute switchToMouse */
970*4104d13fSDmitry Torokhov 		if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) {
971*4104d13fSDmitry Torokhov 			return ret;
972*4104d13fSDmitry Torokhov 		}
973*4104d13fSDmitry Torokhov 	}
974*4104d13fSDmitry Torokhov 
975*4104d13fSDmitry Torokhov 	/* Enable the macro keys */
976*4104d13fSDmitry Torokhov 	if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
977*4104d13fSDmitry Torokhov 		return ret;
978*4104d13fSDmitry Torokhov #if 0
979*4104d13fSDmitry Torokhov 	/* Execute FilterOn */
980*4104d13fSDmitry Torokhov 	if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0)
981*4104d13fSDmitry Torokhov 		return ret;
982*4104d13fSDmitry Torokhov #endif
983*4104d13fSDmitry Torokhov 
984*4104d13fSDmitry Torokhov 	/* Execute AutoGainOn */
985*4104d13fSDmitry Torokhov 	if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0)
986*4104d13fSDmitry Torokhov 		return ret;
987*4104d13fSDmitry Torokhov 
988*4104d13fSDmitry Torokhov 	/* Reset the eventCount, so we track events from last (re)programming
989*4104d13fSDmitry Torokhov 	 */
990*4104d13fSDmitry Torokhov 	aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA;
991*4104d13fSDmitry Torokhov 	aiptek->eventCount = 0;
992*4104d13fSDmitry Torokhov 
993*4104d13fSDmitry Torokhov 	return 0;
994*4104d13fSDmitry Torokhov }
995*4104d13fSDmitry Torokhov 
996*4104d13fSDmitry Torokhov /***********************************************************************
997*4104d13fSDmitry Torokhov  * Sysfs functions. Sysfs prefers that individually-tunable parameters
998*4104d13fSDmitry Torokhov  * exist in their separate pseudo-files. Summary data that is immutable
999*4104d13fSDmitry Torokhov  * may exist in a singular file so long as you don't define a writeable
1000*4104d13fSDmitry Torokhov  * interface.
1001*4104d13fSDmitry Torokhov  */
1002*4104d13fSDmitry Torokhov 
1003*4104d13fSDmitry Torokhov /***********************************************************************
1004*4104d13fSDmitry Torokhov  * support the 'size' file -- display support
1005*4104d13fSDmitry Torokhov  */
1006*4104d13fSDmitry Torokhov static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf)
1007*4104d13fSDmitry Torokhov {
1008*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1009*4104d13fSDmitry Torokhov 
1010*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1011*4104d13fSDmitry Torokhov 		return 0;
1012*4104d13fSDmitry Torokhov 
1013*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%dx%d\n",
1014*4104d13fSDmitry Torokhov 			aiptek->inputdev->absmax[ABS_X] + 1,
1015*4104d13fSDmitry Torokhov 			aiptek->inputdev->absmax[ABS_Y] + 1);
1016*4104d13fSDmitry Torokhov }
1017*4104d13fSDmitry Torokhov 
1018*4104d13fSDmitry Torokhov /* These structs define the sysfs files, param #1 is the name of the
1019*4104d13fSDmitry Torokhov  * file, param 2 is the file permissions, param 3 & 4 are to the
1020*4104d13fSDmitry Torokhov  * output generator and input parser routines. Absence of a routine is
1021*4104d13fSDmitry Torokhov  * permitted -- it only means can't either 'cat' the file, or send data
1022*4104d13fSDmitry Torokhov  * to it.
1023*4104d13fSDmitry Torokhov  */
1024*4104d13fSDmitry Torokhov static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
1025*4104d13fSDmitry Torokhov 
1026*4104d13fSDmitry Torokhov /***********************************************************************
1027*4104d13fSDmitry Torokhov  * support routines for the 'product_id' file
1028*4104d13fSDmitry Torokhov  */
1029*4104d13fSDmitry Torokhov static ssize_t show_tabletProductId(struct device *dev, struct device_attribute *attr, char *buf)
1030*4104d13fSDmitry Torokhov {
1031*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1032*4104d13fSDmitry Torokhov 
1033*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1034*4104d13fSDmitry Torokhov 		return 0;
1035*4104d13fSDmitry Torokhov 
1036*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "0x%04x\n",
1037*4104d13fSDmitry Torokhov 			aiptek->inputdev->id.product);
1038*4104d13fSDmitry Torokhov }
1039*4104d13fSDmitry Torokhov 
1040*4104d13fSDmitry Torokhov static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL);
1041*4104d13fSDmitry Torokhov 
1042*4104d13fSDmitry Torokhov /***********************************************************************
1043*4104d13fSDmitry Torokhov  * support routines for the 'vendor_id' file
1044*4104d13fSDmitry Torokhov  */
1045*4104d13fSDmitry Torokhov static ssize_t show_tabletVendorId(struct device *dev, struct device_attribute *attr, char *buf)
1046*4104d13fSDmitry Torokhov {
1047*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1048*4104d13fSDmitry Torokhov 
1049*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1050*4104d13fSDmitry Torokhov 		return 0;
1051*4104d13fSDmitry Torokhov 
1052*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->inputdev->id.vendor);
1053*4104d13fSDmitry Torokhov }
1054*4104d13fSDmitry Torokhov 
1055*4104d13fSDmitry Torokhov static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL);
1056*4104d13fSDmitry Torokhov 
1057*4104d13fSDmitry Torokhov /***********************************************************************
1058*4104d13fSDmitry Torokhov  * support routines for the 'vendor' file
1059*4104d13fSDmitry Torokhov  */
1060*4104d13fSDmitry Torokhov static ssize_t show_tabletManufacturer(struct device *dev, struct device_attribute *attr, char *buf)
1061*4104d13fSDmitry Torokhov {
1062*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1063*4104d13fSDmitry Torokhov 	int retval;
1064*4104d13fSDmitry Torokhov 
1065*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1066*4104d13fSDmitry Torokhov 		return 0;
1067*4104d13fSDmitry Torokhov 
1068*4104d13fSDmitry Torokhov 	retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer);
1069*4104d13fSDmitry Torokhov 	return retval;
1070*4104d13fSDmitry Torokhov }
1071*4104d13fSDmitry Torokhov 
1072*4104d13fSDmitry Torokhov static DEVICE_ATTR(vendor, S_IRUGO, show_tabletManufacturer, NULL);
1073*4104d13fSDmitry Torokhov 
1074*4104d13fSDmitry Torokhov /***********************************************************************
1075*4104d13fSDmitry Torokhov  * support routines for the 'product' file
1076*4104d13fSDmitry Torokhov  */
1077*4104d13fSDmitry Torokhov static ssize_t show_tabletProduct(struct device *dev, struct device_attribute *attr, char *buf)
1078*4104d13fSDmitry Torokhov {
1079*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1080*4104d13fSDmitry Torokhov 	int retval;
1081*4104d13fSDmitry Torokhov 
1082*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1083*4104d13fSDmitry Torokhov 		return 0;
1084*4104d13fSDmitry Torokhov 
1085*4104d13fSDmitry Torokhov 	retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product);
1086*4104d13fSDmitry Torokhov 	return retval;
1087*4104d13fSDmitry Torokhov }
1088*4104d13fSDmitry Torokhov 
1089*4104d13fSDmitry Torokhov static DEVICE_ATTR(product, S_IRUGO, show_tabletProduct, NULL);
1090*4104d13fSDmitry Torokhov 
1091*4104d13fSDmitry Torokhov /***********************************************************************
1092*4104d13fSDmitry Torokhov  * support routines for the 'pointer_mode' file. Note that this file
1093*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1094*4104d13fSDmitry Torokhov  */
1095*4104d13fSDmitry Torokhov static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf)
1096*4104d13fSDmitry Torokhov {
1097*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1098*4104d13fSDmitry Torokhov 	char *s;
1099*4104d13fSDmitry Torokhov 
1100*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1101*4104d13fSDmitry Torokhov 		return 0;
1102*4104d13fSDmitry Torokhov 
1103*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.pointerMode) {
1104*4104d13fSDmitry Torokhov 	case AIPTEK_POINTER_ONLY_STYLUS_MODE:
1105*4104d13fSDmitry Torokhov 		s = "stylus";
1106*4104d13fSDmitry Torokhov 		break;
1107*4104d13fSDmitry Torokhov 
1108*4104d13fSDmitry Torokhov 	case AIPTEK_POINTER_ONLY_MOUSE_MODE:
1109*4104d13fSDmitry Torokhov 		s = "mouse";
1110*4104d13fSDmitry Torokhov 		break;
1111*4104d13fSDmitry Torokhov 
1112*4104d13fSDmitry Torokhov 	case AIPTEK_POINTER_EITHER_MODE:
1113*4104d13fSDmitry Torokhov 		s = "either";
1114*4104d13fSDmitry Torokhov 		break;
1115*4104d13fSDmitry Torokhov 
1116*4104d13fSDmitry Torokhov 	default:
1117*4104d13fSDmitry Torokhov 		s = "unknown";
1118*4104d13fSDmitry Torokhov 		break;
1119*4104d13fSDmitry Torokhov 	}
1120*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1121*4104d13fSDmitry Torokhov }
1122*4104d13fSDmitry Torokhov 
1123*4104d13fSDmitry Torokhov static ssize_t
1124*4104d13fSDmitry Torokhov store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1125*4104d13fSDmitry Torokhov {
1126*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1127*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1128*4104d13fSDmitry Torokhov 		return 0;
1129*4104d13fSDmitry Torokhov 
1130*4104d13fSDmitry Torokhov 	if (strcmp(buf, "stylus") == 0) {
1131*4104d13fSDmitry Torokhov 		aiptek->newSetting.pointerMode =
1132*4104d13fSDmitry Torokhov 		    AIPTEK_POINTER_ONLY_STYLUS_MODE;
1133*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "mouse") == 0) {
1134*4104d13fSDmitry Torokhov 		aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE;
1135*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "either") == 0) {
1136*4104d13fSDmitry Torokhov 		aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
1137*4104d13fSDmitry Torokhov 	}
1138*4104d13fSDmitry Torokhov 	return count;
1139*4104d13fSDmitry Torokhov }
1140*4104d13fSDmitry Torokhov 
1141*4104d13fSDmitry Torokhov static DEVICE_ATTR(pointer_mode,
1142*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1143*4104d13fSDmitry Torokhov 		   show_tabletPointerMode, store_tabletPointerMode);
1144*4104d13fSDmitry Torokhov 
1145*4104d13fSDmitry Torokhov /***********************************************************************
1146*4104d13fSDmitry Torokhov  * support routines for the 'coordinate_mode' file. Note that this file
1147*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1148*4104d13fSDmitry Torokhov  */
1149*4104d13fSDmitry Torokhov static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf)
1150*4104d13fSDmitry Torokhov {
1151*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1152*4104d13fSDmitry Torokhov 	char *s;
1153*4104d13fSDmitry Torokhov 
1154*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1155*4104d13fSDmitry Torokhov 		return 0;
1156*4104d13fSDmitry Torokhov 
1157*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.coordinateMode) {
1158*4104d13fSDmitry Torokhov 	case AIPTEK_COORDINATE_ABSOLUTE_MODE:
1159*4104d13fSDmitry Torokhov 		s = "absolute";
1160*4104d13fSDmitry Torokhov 		break;
1161*4104d13fSDmitry Torokhov 
1162*4104d13fSDmitry Torokhov 	case AIPTEK_COORDINATE_RELATIVE_MODE:
1163*4104d13fSDmitry Torokhov 		s = "relative";
1164*4104d13fSDmitry Torokhov 		break;
1165*4104d13fSDmitry Torokhov 
1166*4104d13fSDmitry Torokhov 	default:
1167*4104d13fSDmitry Torokhov 		s = "unknown";
1168*4104d13fSDmitry Torokhov 		break;
1169*4104d13fSDmitry Torokhov 	}
1170*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1171*4104d13fSDmitry Torokhov }
1172*4104d13fSDmitry Torokhov 
1173*4104d13fSDmitry Torokhov static ssize_t
1174*4104d13fSDmitry Torokhov store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1175*4104d13fSDmitry Torokhov {
1176*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1177*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1178*4104d13fSDmitry Torokhov 		return 0;
1179*4104d13fSDmitry Torokhov 
1180*4104d13fSDmitry Torokhov 	if (strcmp(buf, "absolute") == 0) {
1181*4104d13fSDmitry Torokhov 		aiptek->newSetting.pointerMode =
1182*4104d13fSDmitry Torokhov 		    AIPTEK_COORDINATE_ABSOLUTE_MODE;
1183*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "relative") == 0) {
1184*4104d13fSDmitry Torokhov 		aiptek->newSetting.pointerMode =
1185*4104d13fSDmitry Torokhov 		    AIPTEK_COORDINATE_RELATIVE_MODE;
1186*4104d13fSDmitry Torokhov 	}
1187*4104d13fSDmitry Torokhov 	return count;
1188*4104d13fSDmitry Torokhov }
1189*4104d13fSDmitry Torokhov 
1190*4104d13fSDmitry Torokhov static DEVICE_ATTR(coordinate_mode,
1191*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1192*4104d13fSDmitry Torokhov 		   show_tabletCoordinateMode, store_tabletCoordinateMode);
1193*4104d13fSDmitry Torokhov 
1194*4104d13fSDmitry Torokhov /***********************************************************************
1195*4104d13fSDmitry Torokhov  * support routines for the 'tool_mode' file. Note that this file
1196*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1197*4104d13fSDmitry Torokhov  */
1198*4104d13fSDmitry Torokhov static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf)
1199*4104d13fSDmitry Torokhov {
1200*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1201*4104d13fSDmitry Torokhov 	char *s;
1202*4104d13fSDmitry Torokhov 
1203*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1204*4104d13fSDmitry Torokhov 		return 0;
1205*4104d13fSDmitry Torokhov 
1206*4104d13fSDmitry Torokhov 	switch (TOOL_BUTTON(aiptek->curSetting.toolMode)) {
1207*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_MOUSE_MODE:
1208*4104d13fSDmitry Torokhov 		s = "mouse";
1209*4104d13fSDmitry Torokhov 		break;
1210*4104d13fSDmitry Torokhov 
1211*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_ERASER_MODE:
1212*4104d13fSDmitry Torokhov 		s = "eraser";
1213*4104d13fSDmitry Torokhov 		break;
1214*4104d13fSDmitry Torokhov 
1215*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_PENCIL_MODE:
1216*4104d13fSDmitry Torokhov 		s = "pencil";
1217*4104d13fSDmitry Torokhov 		break;
1218*4104d13fSDmitry Torokhov 
1219*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_PEN_MODE:
1220*4104d13fSDmitry Torokhov 		s = "pen";
1221*4104d13fSDmitry Torokhov 		break;
1222*4104d13fSDmitry Torokhov 
1223*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_BRUSH_MODE:
1224*4104d13fSDmitry Torokhov 		s = "brush";
1225*4104d13fSDmitry Torokhov 		break;
1226*4104d13fSDmitry Torokhov 
1227*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE:
1228*4104d13fSDmitry Torokhov 		s = "airbrush";
1229*4104d13fSDmitry Torokhov 		break;
1230*4104d13fSDmitry Torokhov 
1231*4104d13fSDmitry Torokhov 	case AIPTEK_TOOL_BUTTON_LENS_MODE:
1232*4104d13fSDmitry Torokhov 		s = "lens";
1233*4104d13fSDmitry Torokhov 		break;
1234*4104d13fSDmitry Torokhov 
1235*4104d13fSDmitry Torokhov 	default:
1236*4104d13fSDmitry Torokhov 		s = "unknown";
1237*4104d13fSDmitry Torokhov 		break;
1238*4104d13fSDmitry Torokhov 	}
1239*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1240*4104d13fSDmitry Torokhov }
1241*4104d13fSDmitry Torokhov 
1242*4104d13fSDmitry Torokhov static ssize_t
1243*4104d13fSDmitry Torokhov store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1244*4104d13fSDmitry Torokhov {
1245*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1246*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1247*4104d13fSDmitry Torokhov 		return 0;
1248*4104d13fSDmitry Torokhov 
1249*4104d13fSDmitry Torokhov 	if (strcmp(buf, "mouse") == 0) {
1250*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE;
1251*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "eraser") == 0) {
1252*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE;
1253*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "pencil") == 0) {
1254*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE;
1255*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "pen") == 0) {
1256*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
1257*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "brush") == 0) {
1258*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE;
1259*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "airbrush") == 0) {
1260*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE;
1261*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "lens") == 0) {
1262*4104d13fSDmitry Torokhov 		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE;
1263*4104d13fSDmitry Torokhov 	}
1264*4104d13fSDmitry Torokhov 
1265*4104d13fSDmitry Torokhov 	return count;
1266*4104d13fSDmitry Torokhov }
1267*4104d13fSDmitry Torokhov 
1268*4104d13fSDmitry Torokhov static DEVICE_ATTR(tool_mode,
1269*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1270*4104d13fSDmitry Torokhov 		   show_tabletToolMode, store_tabletToolMode);
1271*4104d13fSDmitry Torokhov 
1272*4104d13fSDmitry Torokhov /***********************************************************************
1273*4104d13fSDmitry Torokhov  * support routines for the 'xtilt' file. Note that this file
1274*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1275*4104d13fSDmitry Torokhov  */
1276*4104d13fSDmitry Torokhov static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf)
1277*4104d13fSDmitry Torokhov {
1278*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1279*4104d13fSDmitry Torokhov 
1280*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1281*4104d13fSDmitry Torokhov 		return 0;
1282*4104d13fSDmitry Torokhov 
1283*4104d13fSDmitry Torokhov 	if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
1284*4104d13fSDmitry Torokhov 		return snprintf(buf, PAGE_SIZE, "disable\n");
1285*4104d13fSDmitry Torokhov 	} else {
1286*4104d13fSDmitry Torokhov 		return snprintf(buf, PAGE_SIZE, "%d\n",
1287*4104d13fSDmitry Torokhov 				aiptek->curSetting.xTilt);
1288*4104d13fSDmitry Torokhov 	}
1289*4104d13fSDmitry Torokhov }
1290*4104d13fSDmitry Torokhov 
1291*4104d13fSDmitry Torokhov static ssize_t
1292*4104d13fSDmitry Torokhov store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1293*4104d13fSDmitry Torokhov {
1294*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1295*4104d13fSDmitry Torokhov 	int x;
1296*4104d13fSDmitry Torokhov 
1297*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1298*4104d13fSDmitry Torokhov 		return 0;
1299*4104d13fSDmitry Torokhov 
1300*4104d13fSDmitry Torokhov 	if (strcmp(buf, "disable") == 0) {
1301*4104d13fSDmitry Torokhov 		aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE;
1302*4104d13fSDmitry Torokhov 	} else {
1303*4104d13fSDmitry Torokhov 		x = (int)simple_strtol(buf, NULL, 10);
1304*4104d13fSDmitry Torokhov 		if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) {
1305*4104d13fSDmitry Torokhov 			aiptek->newSetting.xTilt = x;
1306*4104d13fSDmitry Torokhov 		}
1307*4104d13fSDmitry Torokhov 	}
1308*4104d13fSDmitry Torokhov 	return count;
1309*4104d13fSDmitry Torokhov }
1310*4104d13fSDmitry Torokhov 
1311*4104d13fSDmitry Torokhov static DEVICE_ATTR(xtilt,
1312*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO, show_tabletXtilt, store_tabletXtilt);
1313*4104d13fSDmitry Torokhov 
1314*4104d13fSDmitry Torokhov /***********************************************************************
1315*4104d13fSDmitry Torokhov  * support routines for the 'ytilt' file. Note that this file
1316*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1317*4104d13fSDmitry Torokhov  */
1318*4104d13fSDmitry Torokhov static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf)
1319*4104d13fSDmitry Torokhov {
1320*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1321*4104d13fSDmitry Torokhov 
1322*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1323*4104d13fSDmitry Torokhov 		return 0;
1324*4104d13fSDmitry Torokhov 
1325*4104d13fSDmitry Torokhov 	if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
1326*4104d13fSDmitry Torokhov 		return snprintf(buf, PAGE_SIZE, "disable\n");
1327*4104d13fSDmitry Torokhov 	} else {
1328*4104d13fSDmitry Torokhov 		return snprintf(buf, PAGE_SIZE, "%d\n",
1329*4104d13fSDmitry Torokhov 				aiptek->curSetting.yTilt);
1330*4104d13fSDmitry Torokhov 	}
1331*4104d13fSDmitry Torokhov }
1332*4104d13fSDmitry Torokhov 
1333*4104d13fSDmitry Torokhov static ssize_t
1334*4104d13fSDmitry Torokhov store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1335*4104d13fSDmitry Torokhov {
1336*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1337*4104d13fSDmitry Torokhov 	int y;
1338*4104d13fSDmitry Torokhov 
1339*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1340*4104d13fSDmitry Torokhov 		return 0;
1341*4104d13fSDmitry Torokhov 
1342*4104d13fSDmitry Torokhov 	if (strcmp(buf, "disable") == 0) {
1343*4104d13fSDmitry Torokhov 		aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE;
1344*4104d13fSDmitry Torokhov 	} else {
1345*4104d13fSDmitry Torokhov 		y = (int)simple_strtol(buf, NULL, 10);
1346*4104d13fSDmitry Torokhov 		if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) {
1347*4104d13fSDmitry Torokhov 			aiptek->newSetting.yTilt = y;
1348*4104d13fSDmitry Torokhov 		}
1349*4104d13fSDmitry Torokhov 	}
1350*4104d13fSDmitry Torokhov 	return count;
1351*4104d13fSDmitry Torokhov }
1352*4104d13fSDmitry Torokhov 
1353*4104d13fSDmitry Torokhov static DEVICE_ATTR(ytilt,
1354*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO, show_tabletYtilt, store_tabletYtilt);
1355*4104d13fSDmitry Torokhov 
1356*4104d13fSDmitry Torokhov /***********************************************************************
1357*4104d13fSDmitry Torokhov  * support routines for the 'jitter' file. Note that this file
1358*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1359*4104d13fSDmitry Torokhov  */
1360*4104d13fSDmitry Torokhov static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf)
1361*4104d13fSDmitry Torokhov {
1362*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1363*4104d13fSDmitry Torokhov 
1364*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1365*4104d13fSDmitry Torokhov 		return 0;
1366*4104d13fSDmitry Torokhov 
1367*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
1368*4104d13fSDmitry Torokhov }
1369*4104d13fSDmitry Torokhov 
1370*4104d13fSDmitry Torokhov static ssize_t
1371*4104d13fSDmitry Torokhov store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1372*4104d13fSDmitry Torokhov {
1373*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1374*4104d13fSDmitry Torokhov 
1375*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1376*4104d13fSDmitry Torokhov 		return 0;
1377*4104d13fSDmitry Torokhov 
1378*4104d13fSDmitry Torokhov 	aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10);
1379*4104d13fSDmitry Torokhov 	return count;
1380*4104d13fSDmitry Torokhov }
1381*4104d13fSDmitry Torokhov 
1382*4104d13fSDmitry Torokhov static DEVICE_ATTR(jitter,
1383*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1384*4104d13fSDmitry Torokhov 		   show_tabletJitterDelay, store_tabletJitterDelay);
1385*4104d13fSDmitry Torokhov 
1386*4104d13fSDmitry Torokhov /***********************************************************************
1387*4104d13fSDmitry Torokhov  * support routines for the 'delay' file. Note that this file
1388*4104d13fSDmitry Torokhov  * both displays current setting and allows reprogramming.
1389*4104d13fSDmitry Torokhov  */
1390*4104d13fSDmitry Torokhov static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf)
1391*4104d13fSDmitry Torokhov {
1392*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1393*4104d13fSDmitry Torokhov 
1394*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1395*4104d13fSDmitry Torokhov 		return 0;
1396*4104d13fSDmitry Torokhov 
1397*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%d\n",
1398*4104d13fSDmitry Torokhov 			aiptek->curSetting.programmableDelay);
1399*4104d13fSDmitry Torokhov }
1400*4104d13fSDmitry Torokhov 
1401*4104d13fSDmitry Torokhov static ssize_t
1402*4104d13fSDmitry Torokhov store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1403*4104d13fSDmitry Torokhov {
1404*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1405*4104d13fSDmitry Torokhov 
1406*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1407*4104d13fSDmitry Torokhov 		return 0;
1408*4104d13fSDmitry Torokhov 
1409*4104d13fSDmitry Torokhov 	aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10);
1410*4104d13fSDmitry Torokhov 	return count;
1411*4104d13fSDmitry Torokhov }
1412*4104d13fSDmitry Torokhov 
1413*4104d13fSDmitry Torokhov static DEVICE_ATTR(delay,
1414*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1415*4104d13fSDmitry Torokhov 		   show_tabletProgrammableDelay, store_tabletProgrammableDelay);
1416*4104d13fSDmitry Torokhov 
1417*4104d13fSDmitry Torokhov /***********************************************************************
1418*4104d13fSDmitry Torokhov  * support routines for the 'input_path' file. Note that this file
1419*4104d13fSDmitry Torokhov  * only displays current setting.
1420*4104d13fSDmitry Torokhov  */
1421*4104d13fSDmitry Torokhov static ssize_t show_tabletInputDevice(struct device *dev, struct device_attribute *attr, char *buf)
1422*4104d13fSDmitry Torokhov {
1423*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1424*4104d13fSDmitry Torokhov 
1425*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1426*4104d13fSDmitry Torokhov 		return 0;
1427*4104d13fSDmitry Torokhov 
1428*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "/dev/input/%s\n",
1429*4104d13fSDmitry Torokhov 			aiptek->features.inputPath);
1430*4104d13fSDmitry Torokhov }
1431*4104d13fSDmitry Torokhov 
1432*4104d13fSDmitry Torokhov static DEVICE_ATTR(input_path, S_IRUGO, show_tabletInputDevice, NULL);
1433*4104d13fSDmitry Torokhov 
1434*4104d13fSDmitry Torokhov /***********************************************************************
1435*4104d13fSDmitry Torokhov  * support routines for the 'event_count' file. Note that this file
1436*4104d13fSDmitry Torokhov  * only displays current setting.
1437*4104d13fSDmitry Torokhov  */
1438*4104d13fSDmitry Torokhov static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf)
1439*4104d13fSDmitry Torokhov {
1440*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1441*4104d13fSDmitry Torokhov 
1442*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1443*4104d13fSDmitry Torokhov 		return 0;
1444*4104d13fSDmitry Torokhov 
1445*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
1446*4104d13fSDmitry Torokhov }
1447*4104d13fSDmitry Torokhov 
1448*4104d13fSDmitry Torokhov static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
1449*4104d13fSDmitry Torokhov 
1450*4104d13fSDmitry Torokhov /***********************************************************************
1451*4104d13fSDmitry Torokhov  * support routines for the 'diagnostic' file. Note that this file
1452*4104d13fSDmitry Torokhov  * only displays current setting.
1453*4104d13fSDmitry Torokhov  */
1454*4104d13fSDmitry Torokhov static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf)
1455*4104d13fSDmitry Torokhov {
1456*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1457*4104d13fSDmitry Torokhov 	char *retMsg;
1458*4104d13fSDmitry Torokhov 
1459*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1460*4104d13fSDmitry Torokhov 		return 0;
1461*4104d13fSDmitry Torokhov 
1462*4104d13fSDmitry Torokhov 	switch (aiptek->diagnostic) {
1463*4104d13fSDmitry Torokhov 	case AIPTEK_DIAGNOSTIC_NA:
1464*4104d13fSDmitry Torokhov 		retMsg = "no errors\n";
1465*4104d13fSDmitry Torokhov 		break;
1466*4104d13fSDmitry Torokhov 
1467*4104d13fSDmitry Torokhov 	case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE:
1468*4104d13fSDmitry Torokhov 		retMsg = "Error: receiving relative reports\n";
1469*4104d13fSDmitry Torokhov 		break;
1470*4104d13fSDmitry Torokhov 
1471*4104d13fSDmitry Torokhov 	case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE:
1472*4104d13fSDmitry Torokhov 		retMsg = "Error: receiving absolute reports\n";
1473*4104d13fSDmitry Torokhov 		break;
1474*4104d13fSDmitry Torokhov 
1475*4104d13fSDmitry Torokhov 	case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED:
1476*4104d13fSDmitry Torokhov 		if (aiptek->curSetting.pointerMode ==
1477*4104d13fSDmitry Torokhov 		    AIPTEK_POINTER_ONLY_MOUSE_MODE) {
1478*4104d13fSDmitry Torokhov 			retMsg = "Error: receiving stylus reports\n";
1479*4104d13fSDmitry Torokhov 		} else {
1480*4104d13fSDmitry Torokhov 			retMsg = "Error: receiving mouse reports\n";
1481*4104d13fSDmitry Torokhov 		}
1482*4104d13fSDmitry Torokhov 		break;
1483*4104d13fSDmitry Torokhov 
1484*4104d13fSDmitry Torokhov 	default:
1485*4104d13fSDmitry Torokhov 		return 0;
1486*4104d13fSDmitry Torokhov 	}
1487*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, retMsg);
1488*4104d13fSDmitry Torokhov }
1489*4104d13fSDmitry Torokhov 
1490*4104d13fSDmitry Torokhov static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
1491*4104d13fSDmitry Torokhov 
1492*4104d13fSDmitry Torokhov /***********************************************************************
1493*4104d13fSDmitry Torokhov  * support routines for the 'stylus_upper' file. Note that this file
1494*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1495*4104d13fSDmitry Torokhov  */
1496*4104d13fSDmitry Torokhov static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf)
1497*4104d13fSDmitry Torokhov {
1498*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1499*4104d13fSDmitry Torokhov 	char *s;
1500*4104d13fSDmitry Torokhov 
1501*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1502*4104d13fSDmitry Torokhov 		return 0;
1503*4104d13fSDmitry Torokhov 
1504*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.stylusButtonUpper) {
1505*4104d13fSDmitry Torokhov 	case AIPTEK_STYLUS_UPPER_BUTTON:
1506*4104d13fSDmitry Torokhov 		s = "upper";
1507*4104d13fSDmitry Torokhov 		break;
1508*4104d13fSDmitry Torokhov 
1509*4104d13fSDmitry Torokhov 	case AIPTEK_STYLUS_LOWER_BUTTON:
1510*4104d13fSDmitry Torokhov 		s = "lower";
1511*4104d13fSDmitry Torokhov 		break;
1512*4104d13fSDmitry Torokhov 
1513*4104d13fSDmitry Torokhov 	default:
1514*4104d13fSDmitry Torokhov 		s = "unknown";
1515*4104d13fSDmitry Torokhov 		break;
1516*4104d13fSDmitry Torokhov 	}
1517*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1518*4104d13fSDmitry Torokhov }
1519*4104d13fSDmitry Torokhov 
1520*4104d13fSDmitry Torokhov static ssize_t
1521*4104d13fSDmitry Torokhov store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1522*4104d13fSDmitry Torokhov {
1523*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1524*4104d13fSDmitry Torokhov 
1525*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1526*4104d13fSDmitry Torokhov 		return 0;
1527*4104d13fSDmitry Torokhov 
1528*4104d13fSDmitry Torokhov 	if (strcmp(buf, "upper") == 0) {
1529*4104d13fSDmitry Torokhov 		aiptek->newSetting.stylusButtonUpper =
1530*4104d13fSDmitry Torokhov 		    AIPTEK_STYLUS_UPPER_BUTTON;
1531*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "lower") == 0) {
1532*4104d13fSDmitry Torokhov 		aiptek->newSetting.stylusButtonUpper =
1533*4104d13fSDmitry Torokhov 		    AIPTEK_STYLUS_LOWER_BUTTON;
1534*4104d13fSDmitry Torokhov 	}
1535*4104d13fSDmitry Torokhov 	return count;
1536*4104d13fSDmitry Torokhov }
1537*4104d13fSDmitry Torokhov 
1538*4104d13fSDmitry Torokhov static DEVICE_ATTR(stylus_upper,
1539*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1540*4104d13fSDmitry Torokhov 		   show_tabletStylusUpper, store_tabletStylusUpper);
1541*4104d13fSDmitry Torokhov 
1542*4104d13fSDmitry Torokhov /***********************************************************************
1543*4104d13fSDmitry Torokhov  * support routines for the 'stylus_lower' file. Note that this file
1544*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1545*4104d13fSDmitry Torokhov  */
1546*4104d13fSDmitry Torokhov static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf)
1547*4104d13fSDmitry Torokhov {
1548*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1549*4104d13fSDmitry Torokhov 	char *s;
1550*4104d13fSDmitry Torokhov 
1551*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1552*4104d13fSDmitry Torokhov 		return 0;
1553*4104d13fSDmitry Torokhov 
1554*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.stylusButtonLower) {
1555*4104d13fSDmitry Torokhov 	case AIPTEK_STYLUS_UPPER_BUTTON:
1556*4104d13fSDmitry Torokhov 		s = "upper";
1557*4104d13fSDmitry Torokhov 		break;
1558*4104d13fSDmitry Torokhov 
1559*4104d13fSDmitry Torokhov 	case AIPTEK_STYLUS_LOWER_BUTTON:
1560*4104d13fSDmitry Torokhov 		s = "lower";
1561*4104d13fSDmitry Torokhov 		break;
1562*4104d13fSDmitry Torokhov 
1563*4104d13fSDmitry Torokhov 	default:
1564*4104d13fSDmitry Torokhov 		s = "unknown";
1565*4104d13fSDmitry Torokhov 		break;
1566*4104d13fSDmitry Torokhov 	}
1567*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1568*4104d13fSDmitry Torokhov }
1569*4104d13fSDmitry Torokhov 
1570*4104d13fSDmitry Torokhov static ssize_t
1571*4104d13fSDmitry Torokhov store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1572*4104d13fSDmitry Torokhov {
1573*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1574*4104d13fSDmitry Torokhov 
1575*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1576*4104d13fSDmitry Torokhov 		return 0;
1577*4104d13fSDmitry Torokhov 
1578*4104d13fSDmitry Torokhov 	if (strcmp(buf, "upper") == 0) {
1579*4104d13fSDmitry Torokhov 		aiptek->newSetting.stylusButtonLower =
1580*4104d13fSDmitry Torokhov 		    AIPTEK_STYLUS_UPPER_BUTTON;
1581*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "lower") == 0) {
1582*4104d13fSDmitry Torokhov 		aiptek->newSetting.stylusButtonLower =
1583*4104d13fSDmitry Torokhov 		    AIPTEK_STYLUS_LOWER_BUTTON;
1584*4104d13fSDmitry Torokhov 	}
1585*4104d13fSDmitry Torokhov 	return count;
1586*4104d13fSDmitry Torokhov }
1587*4104d13fSDmitry Torokhov 
1588*4104d13fSDmitry Torokhov static DEVICE_ATTR(stylus_lower,
1589*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1590*4104d13fSDmitry Torokhov 		   show_tabletStylusLower, store_tabletStylusLower);
1591*4104d13fSDmitry Torokhov 
1592*4104d13fSDmitry Torokhov /***********************************************************************
1593*4104d13fSDmitry Torokhov  * support routines for the 'mouse_left' file. Note that this file
1594*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1595*4104d13fSDmitry Torokhov  */
1596*4104d13fSDmitry Torokhov static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf)
1597*4104d13fSDmitry Torokhov {
1598*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1599*4104d13fSDmitry Torokhov 	char *s;
1600*4104d13fSDmitry Torokhov 
1601*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1602*4104d13fSDmitry Torokhov 		return 0;
1603*4104d13fSDmitry Torokhov 
1604*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.mouseButtonLeft) {
1605*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_LEFT_BUTTON:
1606*4104d13fSDmitry Torokhov 		s = "left";
1607*4104d13fSDmitry Torokhov 		break;
1608*4104d13fSDmitry Torokhov 
1609*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_MIDDLE_BUTTON:
1610*4104d13fSDmitry Torokhov 		s = "middle";
1611*4104d13fSDmitry Torokhov 		break;
1612*4104d13fSDmitry Torokhov 
1613*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_RIGHT_BUTTON:
1614*4104d13fSDmitry Torokhov 		s = "right";
1615*4104d13fSDmitry Torokhov 		break;
1616*4104d13fSDmitry Torokhov 
1617*4104d13fSDmitry Torokhov 	default:
1618*4104d13fSDmitry Torokhov 		s = "unknown";
1619*4104d13fSDmitry Torokhov 		break;
1620*4104d13fSDmitry Torokhov 	}
1621*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1622*4104d13fSDmitry Torokhov }
1623*4104d13fSDmitry Torokhov 
1624*4104d13fSDmitry Torokhov static ssize_t
1625*4104d13fSDmitry Torokhov store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1626*4104d13fSDmitry Torokhov {
1627*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1628*4104d13fSDmitry Torokhov 
1629*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1630*4104d13fSDmitry Torokhov 		return 0;
1631*4104d13fSDmitry Torokhov 
1632*4104d13fSDmitry Torokhov 	if (strcmp(buf, "left") == 0) {
1633*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
1634*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "middle") == 0) {
1635*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON;
1636*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "right") == 0) {
1637*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON;
1638*4104d13fSDmitry Torokhov 	}
1639*4104d13fSDmitry Torokhov 	return count;
1640*4104d13fSDmitry Torokhov }
1641*4104d13fSDmitry Torokhov 
1642*4104d13fSDmitry Torokhov static DEVICE_ATTR(mouse_left,
1643*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1644*4104d13fSDmitry Torokhov 		   show_tabletMouseLeft, store_tabletMouseLeft);
1645*4104d13fSDmitry Torokhov 
1646*4104d13fSDmitry Torokhov /***********************************************************************
1647*4104d13fSDmitry Torokhov  * support routines for the 'mouse_middle' file. Note that this file
1648*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1649*4104d13fSDmitry Torokhov  */
1650*4104d13fSDmitry Torokhov static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf)
1651*4104d13fSDmitry Torokhov {
1652*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1653*4104d13fSDmitry Torokhov 	char *s;
1654*4104d13fSDmitry Torokhov 
1655*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1656*4104d13fSDmitry Torokhov 		return 0;
1657*4104d13fSDmitry Torokhov 
1658*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.mouseButtonMiddle) {
1659*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_LEFT_BUTTON:
1660*4104d13fSDmitry Torokhov 		s = "left";
1661*4104d13fSDmitry Torokhov 		break;
1662*4104d13fSDmitry Torokhov 
1663*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_MIDDLE_BUTTON:
1664*4104d13fSDmitry Torokhov 		s = "middle";
1665*4104d13fSDmitry Torokhov 		break;
1666*4104d13fSDmitry Torokhov 
1667*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_RIGHT_BUTTON:
1668*4104d13fSDmitry Torokhov 		s = "right";
1669*4104d13fSDmitry Torokhov 		break;
1670*4104d13fSDmitry Torokhov 
1671*4104d13fSDmitry Torokhov 	default:
1672*4104d13fSDmitry Torokhov 		s = "unknown";
1673*4104d13fSDmitry Torokhov 		break;
1674*4104d13fSDmitry Torokhov 	}
1675*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1676*4104d13fSDmitry Torokhov }
1677*4104d13fSDmitry Torokhov 
1678*4104d13fSDmitry Torokhov static ssize_t
1679*4104d13fSDmitry Torokhov store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1680*4104d13fSDmitry Torokhov {
1681*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1682*4104d13fSDmitry Torokhov 
1683*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1684*4104d13fSDmitry Torokhov 		return 0;
1685*4104d13fSDmitry Torokhov 
1686*4104d13fSDmitry Torokhov 	if (strcmp(buf, "left") == 0) {
1687*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonMiddle = AIPTEK_MOUSE_LEFT_BUTTON;
1688*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "middle") == 0) {
1689*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonMiddle =
1690*4104d13fSDmitry Torokhov 		    AIPTEK_MOUSE_MIDDLE_BUTTON;
1691*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "right") == 0) {
1692*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonMiddle =
1693*4104d13fSDmitry Torokhov 		    AIPTEK_MOUSE_RIGHT_BUTTON;
1694*4104d13fSDmitry Torokhov 	}
1695*4104d13fSDmitry Torokhov 	return count;
1696*4104d13fSDmitry Torokhov }
1697*4104d13fSDmitry Torokhov 
1698*4104d13fSDmitry Torokhov static DEVICE_ATTR(mouse_middle,
1699*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1700*4104d13fSDmitry Torokhov 		   show_tabletMouseMiddle, store_tabletMouseMiddle);
1701*4104d13fSDmitry Torokhov 
1702*4104d13fSDmitry Torokhov /***********************************************************************
1703*4104d13fSDmitry Torokhov  * support routines for the 'mouse_right' file. Note that this file
1704*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1705*4104d13fSDmitry Torokhov  */
1706*4104d13fSDmitry Torokhov static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf)
1707*4104d13fSDmitry Torokhov {
1708*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1709*4104d13fSDmitry Torokhov 	char *s;
1710*4104d13fSDmitry Torokhov 
1711*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1712*4104d13fSDmitry Torokhov 		return 0;
1713*4104d13fSDmitry Torokhov 
1714*4104d13fSDmitry Torokhov 	switch (aiptek->curSetting.mouseButtonRight) {
1715*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_LEFT_BUTTON:
1716*4104d13fSDmitry Torokhov 		s = "left";
1717*4104d13fSDmitry Torokhov 		break;
1718*4104d13fSDmitry Torokhov 
1719*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_MIDDLE_BUTTON:
1720*4104d13fSDmitry Torokhov 		s = "middle";
1721*4104d13fSDmitry Torokhov 		break;
1722*4104d13fSDmitry Torokhov 
1723*4104d13fSDmitry Torokhov 	case AIPTEK_MOUSE_RIGHT_BUTTON:
1724*4104d13fSDmitry Torokhov 		s = "right";
1725*4104d13fSDmitry Torokhov 		break;
1726*4104d13fSDmitry Torokhov 
1727*4104d13fSDmitry Torokhov 	default:
1728*4104d13fSDmitry Torokhov 		s = "unknown";
1729*4104d13fSDmitry Torokhov 		break;
1730*4104d13fSDmitry Torokhov 	}
1731*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%s\n", s);
1732*4104d13fSDmitry Torokhov }
1733*4104d13fSDmitry Torokhov 
1734*4104d13fSDmitry Torokhov static ssize_t
1735*4104d13fSDmitry Torokhov store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1736*4104d13fSDmitry Torokhov {
1737*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1738*4104d13fSDmitry Torokhov 
1739*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1740*4104d13fSDmitry Torokhov 		return 0;
1741*4104d13fSDmitry Torokhov 
1742*4104d13fSDmitry Torokhov 	if (strcmp(buf, "left") == 0) {
1743*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_LEFT_BUTTON;
1744*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "middle") == 0) {
1745*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonRight =
1746*4104d13fSDmitry Torokhov 		    AIPTEK_MOUSE_MIDDLE_BUTTON;
1747*4104d13fSDmitry Torokhov 	} else if (strcmp(buf, "right") == 0) {
1748*4104d13fSDmitry Torokhov 		aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
1749*4104d13fSDmitry Torokhov 	}
1750*4104d13fSDmitry Torokhov 	return count;
1751*4104d13fSDmitry Torokhov }
1752*4104d13fSDmitry Torokhov 
1753*4104d13fSDmitry Torokhov static DEVICE_ATTR(mouse_right,
1754*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO,
1755*4104d13fSDmitry Torokhov 		   show_tabletMouseRight, store_tabletMouseRight);
1756*4104d13fSDmitry Torokhov 
1757*4104d13fSDmitry Torokhov /***********************************************************************
1758*4104d13fSDmitry Torokhov  * support routines for the 'wheel' file. Note that this file
1759*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1760*4104d13fSDmitry Torokhov  */
1761*4104d13fSDmitry Torokhov static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf)
1762*4104d13fSDmitry Torokhov {
1763*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1764*4104d13fSDmitry Torokhov 
1765*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1766*4104d13fSDmitry Torokhov 		return 0;
1767*4104d13fSDmitry Torokhov 
1768*4104d13fSDmitry Torokhov 	if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
1769*4104d13fSDmitry Torokhov 		return snprintf(buf, PAGE_SIZE, "disable\n");
1770*4104d13fSDmitry Torokhov 	} else {
1771*4104d13fSDmitry Torokhov 		return snprintf(buf, PAGE_SIZE, "%d\n",
1772*4104d13fSDmitry Torokhov 				aiptek->curSetting.wheel);
1773*4104d13fSDmitry Torokhov 	}
1774*4104d13fSDmitry Torokhov }
1775*4104d13fSDmitry Torokhov 
1776*4104d13fSDmitry Torokhov static ssize_t
1777*4104d13fSDmitry Torokhov store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1778*4104d13fSDmitry Torokhov {
1779*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1780*4104d13fSDmitry Torokhov 
1781*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1782*4104d13fSDmitry Torokhov 		return 0;
1783*4104d13fSDmitry Torokhov 
1784*4104d13fSDmitry Torokhov 	aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10);
1785*4104d13fSDmitry Torokhov 	return count;
1786*4104d13fSDmitry Torokhov }
1787*4104d13fSDmitry Torokhov 
1788*4104d13fSDmitry Torokhov static DEVICE_ATTR(wheel,
1789*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO, show_tabletWheel, store_tabletWheel);
1790*4104d13fSDmitry Torokhov 
1791*4104d13fSDmitry Torokhov /***********************************************************************
1792*4104d13fSDmitry Torokhov  * support routines for the 'execute' file. Note that this file
1793*4104d13fSDmitry Torokhov  * both displays current setting and allows for setting changing.
1794*4104d13fSDmitry Torokhov  */
1795*4104d13fSDmitry Torokhov static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf)
1796*4104d13fSDmitry Torokhov {
1797*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1798*4104d13fSDmitry Torokhov 
1799*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1800*4104d13fSDmitry Torokhov 		return 0;
1801*4104d13fSDmitry Torokhov 
1802*4104d13fSDmitry Torokhov 	/* There is nothing useful to display, so a one-line manual
1803*4104d13fSDmitry Torokhov 	 * is in order...
1804*4104d13fSDmitry Torokhov 	 */
1805*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE,
1806*4104d13fSDmitry Torokhov 			"Write anything to this file to program your tablet.\n");
1807*4104d13fSDmitry Torokhov }
1808*4104d13fSDmitry Torokhov 
1809*4104d13fSDmitry Torokhov static ssize_t
1810*4104d13fSDmitry Torokhov store_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1811*4104d13fSDmitry Torokhov {
1812*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1813*4104d13fSDmitry Torokhov 
1814*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1815*4104d13fSDmitry Torokhov 		return 0;
1816*4104d13fSDmitry Torokhov 
1817*4104d13fSDmitry Torokhov 	/* We do not care what you write to this file. Merely the action
1818*4104d13fSDmitry Torokhov 	 * of writing to this file triggers a tablet reprogramming.
1819*4104d13fSDmitry Torokhov 	 */
1820*4104d13fSDmitry Torokhov 	memcpy(&aiptek->curSetting, &aiptek->newSetting,
1821*4104d13fSDmitry Torokhov 	       sizeof(struct aiptek_settings));
1822*4104d13fSDmitry Torokhov 
1823*4104d13fSDmitry Torokhov 	if (aiptek_program_tablet(aiptek) < 0)
1824*4104d13fSDmitry Torokhov 		return -EIO;
1825*4104d13fSDmitry Torokhov 
1826*4104d13fSDmitry Torokhov 	return count;
1827*4104d13fSDmitry Torokhov }
1828*4104d13fSDmitry Torokhov 
1829*4104d13fSDmitry Torokhov static DEVICE_ATTR(execute,
1830*4104d13fSDmitry Torokhov 		   S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute);
1831*4104d13fSDmitry Torokhov 
1832*4104d13fSDmitry Torokhov /***********************************************************************
1833*4104d13fSDmitry Torokhov  * support routines for the 'odm_code' file. Note that this file
1834*4104d13fSDmitry Torokhov  * only displays current setting.
1835*4104d13fSDmitry Torokhov  */
1836*4104d13fSDmitry Torokhov static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf)
1837*4104d13fSDmitry Torokhov {
1838*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1839*4104d13fSDmitry Torokhov 
1840*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1841*4104d13fSDmitry Torokhov 		return 0;
1842*4104d13fSDmitry Torokhov 
1843*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
1844*4104d13fSDmitry Torokhov }
1845*4104d13fSDmitry Torokhov 
1846*4104d13fSDmitry Torokhov static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
1847*4104d13fSDmitry Torokhov 
1848*4104d13fSDmitry Torokhov /***********************************************************************
1849*4104d13fSDmitry Torokhov  * support routines for the 'model_code' file. Note that this file
1850*4104d13fSDmitry Torokhov  * only displays current setting.
1851*4104d13fSDmitry Torokhov  */
1852*4104d13fSDmitry Torokhov static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf)
1853*4104d13fSDmitry Torokhov {
1854*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1855*4104d13fSDmitry Torokhov 
1856*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1857*4104d13fSDmitry Torokhov 		return 0;
1858*4104d13fSDmitry Torokhov 
1859*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
1860*4104d13fSDmitry Torokhov }
1861*4104d13fSDmitry Torokhov 
1862*4104d13fSDmitry Torokhov static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
1863*4104d13fSDmitry Torokhov 
1864*4104d13fSDmitry Torokhov /***********************************************************************
1865*4104d13fSDmitry Torokhov  * support routines for the 'firmware_code' file. Note that this file
1866*4104d13fSDmitry Torokhov  * only displays current setting.
1867*4104d13fSDmitry Torokhov  */
1868*4104d13fSDmitry Torokhov static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf)
1869*4104d13fSDmitry Torokhov {
1870*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = dev_get_drvdata(dev);
1871*4104d13fSDmitry Torokhov 
1872*4104d13fSDmitry Torokhov 	if (aiptek == NULL)
1873*4104d13fSDmitry Torokhov 		return 0;
1874*4104d13fSDmitry Torokhov 
1875*4104d13fSDmitry Torokhov 	return snprintf(buf, PAGE_SIZE, "%04x\n",
1876*4104d13fSDmitry Torokhov 			aiptek->features.firmwareCode);
1877*4104d13fSDmitry Torokhov }
1878*4104d13fSDmitry Torokhov 
1879*4104d13fSDmitry Torokhov static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
1880*4104d13fSDmitry Torokhov 
1881*4104d13fSDmitry Torokhov /***********************************************************************
1882*4104d13fSDmitry Torokhov  * This routine removes all existing sysfs files managed by this device
1883*4104d13fSDmitry Torokhov  * driver.
1884*4104d13fSDmitry Torokhov  */
1885*4104d13fSDmitry Torokhov static void aiptek_delete_files(struct device *dev)
1886*4104d13fSDmitry Torokhov {
1887*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_size);
1888*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_product_id);
1889*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_vendor_id);
1890*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_vendor);
1891*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_product);
1892*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_pointer_mode);
1893*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_coordinate_mode);
1894*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_tool_mode);
1895*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_xtilt);
1896*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_ytilt);
1897*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_jitter);
1898*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_delay);
1899*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_input_path);
1900*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_event_count);
1901*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_diagnostic);
1902*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_odm_code);
1903*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_model_code);
1904*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_firmware_code);
1905*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_stylus_lower);
1906*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_stylus_upper);
1907*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_mouse_left);
1908*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_mouse_middle);
1909*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_mouse_right);
1910*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_wheel);
1911*4104d13fSDmitry Torokhov 	device_remove_file(dev, &dev_attr_execute);
1912*4104d13fSDmitry Torokhov }
1913*4104d13fSDmitry Torokhov 
1914*4104d13fSDmitry Torokhov /***********************************************************************
1915*4104d13fSDmitry Torokhov  * This routine creates the sysfs files managed by this device
1916*4104d13fSDmitry Torokhov  * driver.
1917*4104d13fSDmitry Torokhov  */
1918*4104d13fSDmitry Torokhov static int aiptek_add_files(struct device *dev)
1919*4104d13fSDmitry Torokhov {
1920*4104d13fSDmitry Torokhov 	int ret;
1921*4104d13fSDmitry Torokhov 
1922*4104d13fSDmitry Torokhov 	if ((ret = device_create_file(dev, &dev_attr_size)) ||
1923*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_product_id)) ||
1924*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_vendor_id)) ||
1925*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_vendor)) ||
1926*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_product)) ||
1927*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_pointer_mode)) ||
1928*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_coordinate_mode)) ||
1929*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_tool_mode)) ||
1930*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_xtilt)) ||
1931*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_ytilt)) ||
1932*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_jitter)) ||
1933*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_delay)) ||
1934*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_input_path)) ||
1935*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_event_count)) ||
1936*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_diagnostic)) ||
1937*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_odm_code)) ||
1938*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_model_code)) ||
1939*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_firmware_code)) ||
1940*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_stylus_lower)) ||
1941*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_stylus_upper)) ||
1942*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_mouse_left)) ||
1943*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_mouse_middle)) ||
1944*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_mouse_right)) ||
1945*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_wheel)) ||
1946*4104d13fSDmitry Torokhov 	    (ret = device_create_file(dev, &dev_attr_execute))) {
1947*4104d13fSDmitry Torokhov 		err("aiptek: killing own sysfs device files\n");
1948*4104d13fSDmitry Torokhov 		aiptek_delete_files(dev);
1949*4104d13fSDmitry Torokhov 	}
1950*4104d13fSDmitry Torokhov 	return ret;
1951*4104d13fSDmitry Torokhov }
1952*4104d13fSDmitry Torokhov 
1953*4104d13fSDmitry Torokhov /***********************************************************************
1954*4104d13fSDmitry Torokhov  * This routine is called when a tablet has been identified. It basically
1955*4104d13fSDmitry Torokhov  * sets up the tablet and the driver's internal structures.
1956*4104d13fSDmitry Torokhov  */
1957*4104d13fSDmitry Torokhov static int
1958*4104d13fSDmitry Torokhov aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
1959*4104d13fSDmitry Torokhov {
1960*4104d13fSDmitry Torokhov 	struct usb_device *usbdev = interface_to_usbdev(intf);
1961*4104d13fSDmitry Torokhov 	struct usb_endpoint_descriptor *endpoint;
1962*4104d13fSDmitry Torokhov 	struct aiptek *aiptek;
1963*4104d13fSDmitry Torokhov 	struct input_dev *inputdev;
1964*4104d13fSDmitry Torokhov 	struct input_handle *inputhandle;
1965*4104d13fSDmitry Torokhov 	struct list_head *node, *next;
1966*4104d13fSDmitry Torokhov 	int i;
1967*4104d13fSDmitry Torokhov 	int speeds[] = { 0,
1968*4104d13fSDmitry Torokhov 		AIPTEK_PROGRAMMABLE_DELAY_50,
1969*4104d13fSDmitry Torokhov 		AIPTEK_PROGRAMMABLE_DELAY_400,
1970*4104d13fSDmitry Torokhov 		AIPTEK_PROGRAMMABLE_DELAY_25,
1971*4104d13fSDmitry Torokhov 		AIPTEK_PROGRAMMABLE_DELAY_100,
1972*4104d13fSDmitry Torokhov 		AIPTEK_PROGRAMMABLE_DELAY_200,
1973*4104d13fSDmitry Torokhov 		AIPTEK_PROGRAMMABLE_DELAY_300
1974*4104d13fSDmitry Torokhov 	};
1975*4104d13fSDmitry Torokhov 	int err = -ENOMEM;
1976*4104d13fSDmitry Torokhov 
1977*4104d13fSDmitry Torokhov 	/* programmableDelay is where the command-line specified
1978*4104d13fSDmitry Torokhov 	 * delay is kept. We make it the first element of speeds[],
1979*4104d13fSDmitry Torokhov 	 * so therefore, your override speed is tried first, then the
1980*4104d13fSDmitry Torokhov 	 * remainder. Note that the default value of 400ms will be tried
1981*4104d13fSDmitry Torokhov 	 * if you do not specify any command line parameter.
1982*4104d13fSDmitry Torokhov 	 */
1983*4104d13fSDmitry Torokhov 	speeds[0] = programmableDelay;
1984*4104d13fSDmitry Torokhov 
1985*4104d13fSDmitry Torokhov 	aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL);
1986*4104d13fSDmitry Torokhov 	inputdev = input_allocate_device();
1987*4104d13fSDmitry Torokhov 	if (!aiptek || !inputdev)
1988*4104d13fSDmitry Torokhov 		goto fail1;
1989*4104d13fSDmitry Torokhov 
1990*4104d13fSDmitry Torokhov 	aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH,
1991*4104d13fSDmitry Torokhov 					GFP_ATOMIC, &aiptek->data_dma);
1992*4104d13fSDmitry Torokhov 	if (!aiptek->data)
1993*4104d13fSDmitry Torokhov 		goto fail1;
1994*4104d13fSDmitry Torokhov 
1995*4104d13fSDmitry Torokhov 	aiptek->urb = usb_alloc_urb(0, GFP_KERNEL);
1996*4104d13fSDmitry Torokhov 	if (!aiptek->urb)
1997*4104d13fSDmitry Torokhov 		goto fail2;
1998*4104d13fSDmitry Torokhov 
1999*4104d13fSDmitry Torokhov 	aiptek->inputdev = inputdev;
2000*4104d13fSDmitry Torokhov 	aiptek->usbdev = usbdev;
2001*4104d13fSDmitry Torokhov 	aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber;
2002*4104d13fSDmitry Torokhov 	aiptek->inDelay = 0;
2003*4104d13fSDmitry Torokhov 	aiptek->endDelay = 0;
2004*4104d13fSDmitry Torokhov 	aiptek->previousJitterable = 0;
2005*4104d13fSDmitry Torokhov 
2006*4104d13fSDmitry Torokhov 	/* Set up the curSettings struct. Said struct contains the current
2007*4104d13fSDmitry Torokhov 	 * programmable parameters. The newSetting struct contains changes
2008*4104d13fSDmitry Torokhov 	 * the user makes to the settings via the sysfs interface. Those
2009*4104d13fSDmitry Torokhov 	 * changes are not "committed" to curSettings until the user
2010*4104d13fSDmitry Torokhov 	 * writes to the sysfs/.../execute file.
2011*4104d13fSDmitry Torokhov 	 */
2012*4104d13fSDmitry Torokhov 	aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
2013*4104d13fSDmitry Torokhov 	aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE;
2014*4104d13fSDmitry Torokhov 	aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
2015*4104d13fSDmitry Torokhov 	aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE;
2016*4104d13fSDmitry Torokhov 	aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE;
2017*4104d13fSDmitry Torokhov 	aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
2018*4104d13fSDmitry Torokhov 	aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON;
2019*4104d13fSDmitry Torokhov 	aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
2020*4104d13fSDmitry Torokhov 	aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON;
2021*4104d13fSDmitry Torokhov 	aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON;
2022*4104d13fSDmitry Torokhov 	aiptek->curSetting.jitterDelay = jitterDelay;
2023*4104d13fSDmitry Torokhov 	aiptek->curSetting.programmableDelay = programmableDelay;
2024*4104d13fSDmitry Torokhov 
2025*4104d13fSDmitry Torokhov 	/* Both structs should have equivalent settings
2026*4104d13fSDmitry Torokhov 	 */
2027*4104d13fSDmitry Torokhov 	aiptek->newSetting = aiptek->curSetting;
2028*4104d13fSDmitry Torokhov 
2029*4104d13fSDmitry Torokhov 	/* Determine the usb devices' physical path.
2030*4104d13fSDmitry Torokhov 	 * Asketh not why we always pretend we're using "../input0",
2031*4104d13fSDmitry Torokhov 	 * but I suspect this will have to be refactored one
2032*4104d13fSDmitry Torokhov 	 * day if a single USB device can be a keyboard & a mouse
2033*4104d13fSDmitry Torokhov 	 * & a tablet, and the inputX number actually will tell
2034*4104d13fSDmitry Torokhov 	 * us something...
2035*4104d13fSDmitry Torokhov 	 */
2036*4104d13fSDmitry Torokhov 	usb_make_path(usbdev, aiptek->features.usbPath,
2037*4104d13fSDmitry Torokhov 			sizeof(aiptek->features.usbPath));
2038*4104d13fSDmitry Torokhov 	strlcat(aiptek->features.usbPath, "/input0",
2039*4104d13fSDmitry Torokhov 		sizeof(aiptek->features.usbPath));
2040*4104d13fSDmitry Torokhov 
2041*4104d13fSDmitry Torokhov 	/* Set up client data, pointers to open and close routines
2042*4104d13fSDmitry Torokhov 	 * for the input device.
2043*4104d13fSDmitry Torokhov 	 */
2044*4104d13fSDmitry Torokhov 	inputdev->name = "Aiptek";
2045*4104d13fSDmitry Torokhov 	inputdev->phys = aiptek->features.usbPath;
2046*4104d13fSDmitry Torokhov 	usb_to_input_id(usbdev, &inputdev->id);
2047*4104d13fSDmitry Torokhov 	inputdev->dev.parent = &intf->dev;
2048*4104d13fSDmitry Torokhov 
2049*4104d13fSDmitry Torokhov 	input_set_drvdata(inputdev, aiptek);
2050*4104d13fSDmitry Torokhov 
2051*4104d13fSDmitry Torokhov 	inputdev->open = aiptek_open;
2052*4104d13fSDmitry Torokhov 	inputdev->close = aiptek_close;
2053*4104d13fSDmitry Torokhov 
2054*4104d13fSDmitry Torokhov 	/* Now program the capacities of the tablet, in terms of being
2055*4104d13fSDmitry Torokhov 	 * an input device.
2056*4104d13fSDmitry Torokhov 	 */
2057*4104d13fSDmitry Torokhov 	inputdev->evbit[0] |= BIT(EV_KEY)
2058*4104d13fSDmitry Torokhov 	    | BIT(EV_ABS)
2059*4104d13fSDmitry Torokhov 	    | BIT(EV_REL)
2060*4104d13fSDmitry Torokhov 	    | BIT(EV_MSC);
2061*4104d13fSDmitry Torokhov 
2062*4104d13fSDmitry Torokhov 	inputdev->absbit[0] |= BIT(ABS_MISC);
2063*4104d13fSDmitry Torokhov 
2064*4104d13fSDmitry Torokhov 	inputdev->relbit[0] |=
2065*4104d13fSDmitry Torokhov 	    (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC));
2066*4104d13fSDmitry Torokhov 
2067*4104d13fSDmitry Torokhov 	inputdev->keybit[LONG(BTN_LEFT)] |=
2068*4104d13fSDmitry Torokhov 	    (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE));
2069*4104d13fSDmitry Torokhov 
2070*4104d13fSDmitry Torokhov 	inputdev->keybit[LONG(BTN_DIGI)] |=
2071*4104d13fSDmitry Torokhov 	    (BIT(BTN_TOOL_PEN) |
2072*4104d13fSDmitry Torokhov 	     BIT(BTN_TOOL_RUBBER) |
2073*4104d13fSDmitry Torokhov 	     BIT(BTN_TOOL_PENCIL) |
2074*4104d13fSDmitry Torokhov 	     BIT(BTN_TOOL_AIRBRUSH) |
2075*4104d13fSDmitry Torokhov 	     BIT(BTN_TOOL_BRUSH) |
2076*4104d13fSDmitry Torokhov 	     BIT(BTN_TOOL_MOUSE) |
2077*4104d13fSDmitry Torokhov 	     BIT(BTN_TOOL_LENS) |
2078*4104d13fSDmitry Torokhov 	     BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2));
2079*4104d13fSDmitry Torokhov 
2080*4104d13fSDmitry Torokhov 	inputdev->mscbit[0] = BIT(MSC_SERIAL);
2081*4104d13fSDmitry Torokhov 
2082*4104d13fSDmitry Torokhov 	/* Programming the tablet macro keys needs to be done with a for loop
2083*4104d13fSDmitry Torokhov 	 * as the keycodes are discontiguous.
2084*4104d13fSDmitry Torokhov 	 */
2085*4104d13fSDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
2086*4104d13fSDmitry Torokhov 		set_bit(macroKeyEvents[i], inputdev->keybit);
2087*4104d13fSDmitry Torokhov 
2088*4104d13fSDmitry Torokhov 	/*
2089*4104d13fSDmitry Torokhov 	 * Program the input device coordinate capacities. We do not yet
2090*4104d13fSDmitry Torokhov 	 * know what maximum X, Y, and Z values are, so we're putting fake
2091*4104d13fSDmitry Torokhov 	 * values in. Later, we'll ask the tablet to put in the correct
2092*4104d13fSDmitry Torokhov 	 * values.
2093*4104d13fSDmitry Torokhov 	 */
2094*4104d13fSDmitry Torokhov 	input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0);
2095*4104d13fSDmitry Torokhov 	input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0);
2096*4104d13fSDmitry Torokhov 	input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0);
2097*4104d13fSDmitry Torokhov 	input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
2098*4104d13fSDmitry Torokhov 	input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
2099*4104d13fSDmitry Torokhov 	input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0);
2100*4104d13fSDmitry Torokhov 
2101*4104d13fSDmitry Torokhov 	endpoint = &intf->altsetting[0].endpoint[0].desc;
2102*4104d13fSDmitry Torokhov 
2103*4104d13fSDmitry Torokhov 	/* Go set up our URB, which is called when the tablet receives
2104*4104d13fSDmitry Torokhov 	 * input.
2105*4104d13fSDmitry Torokhov 	 */
2106*4104d13fSDmitry Torokhov 	usb_fill_int_urb(aiptek->urb,
2107*4104d13fSDmitry Torokhov 			 aiptek->usbdev,
2108*4104d13fSDmitry Torokhov 			 usb_rcvintpipe(aiptek->usbdev,
2109*4104d13fSDmitry Torokhov 					endpoint->bEndpointAddress),
2110*4104d13fSDmitry Torokhov 			 aiptek->data, 8, aiptek_irq, aiptek,
2111*4104d13fSDmitry Torokhov 			 endpoint->bInterval);
2112*4104d13fSDmitry Torokhov 
2113*4104d13fSDmitry Torokhov 	aiptek->urb->transfer_dma = aiptek->data_dma;
2114*4104d13fSDmitry Torokhov 	aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
2115*4104d13fSDmitry Torokhov 
2116*4104d13fSDmitry Torokhov 	/* Program the tablet. This sets the tablet up in the mode
2117*4104d13fSDmitry Torokhov 	 * specified in newSetting, and also queries the tablet's
2118*4104d13fSDmitry Torokhov 	 * physical capacities.
2119*4104d13fSDmitry Torokhov 	 *
2120*4104d13fSDmitry Torokhov 	 * Sanity check: if a tablet doesn't like the slow programmatic
2121*4104d13fSDmitry Torokhov 	 * delay, we often get sizes of 0x0. Let's use that as an indicator
2122*4104d13fSDmitry Torokhov 	 * to try faster delays, up to 25 ms. If that logic fails, well, you'll
2123*4104d13fSDmitry Torokhov 	 * have to explain to us how your tablet thinks it's 0x0, and yet that's
2124*4104d13fSDmitry Torokhov 	 * not an error :-)
2125*4104d13fSDmitry Torokhov 	 */
2126*4104d13fSDmitry Torokhov 
2127*4104d13fSDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
2128*4104d13fSDmitry Torokhov 		aiptek->curSetting.programmableDelay = speeds[i];
2129*4104d13fSDmitry Torokhov 		(void)aiptek_program_tablet(aiptek);
2130*4104d13fSDmitry Torokhov 		if (aiptek->inputdev->absmax[ABS_X] > 0) {
2131*4104d13fSDmitry Torokhov 			info("input: Aiptek using %d ms programming speed\n",
2132*4104d13fSDmitry Torokhov 			     aiptek->curSetting.programmableDelay);
2133*4104d13fSDmitry Torokhov 			break;
2134*4104d13fSDmitry Torokhov 		}
2135*4104d13fSDmitry Torokhov 	}
2136*4104d13fSDmitry Torokhov 
2137*4104d13fSDmitry Torokhov 	/* Register the tablet as an Input Device
2138*4104d13fSDmitry Torokhov 	 */
2139*4104d13fSDmitry Torokhov 	err = input_register_device(aiptek->inputdev);
2140*4104d13fSDmitry Torokhov 	if (err)
2141*4104d13fSDmitry Torokhov 		goto fail2;
2142*4104d13fSDmitry Torokhov 
2143*4104d13fSDmitry Torokhov 	/* We now will look for the evdev device which is mapped to
2144*4104d13fSDmitry Torokhov 	 * the tablet. The partial name is kept in the link list of
2145*4104d13fSDmitry Torokhov 	 * input_handles associated with this input device.
2146*4104d13fSDmitry Torokhov 	 * What identifies an evdev input_handler is that it begins
2147*4104d13fSDmitry Torokhov 	 * with 'event', continues with a digit, and that in turn
2148*4104d13fSDmitry Torokhov 	 * is mapped to input/eventN.
2149*4104d13fSDmitry Torokhov 	 */
2150*4104d13fSDmitry Torokhov 	list_for_each_safe(node, next, &inputdev->h_list) {
2151*4104d13fSDmitry Torokhov 		inputhandle = to_handle(node);
2152*4104d13fSDmitry Torokhov 		if (strncmp(inputhandle->name, "event", 5) == 0) {
2153*4104d13fSDmitry Torokhov 			strcpy(aiptek->features.inputPath, inputhandle->name);
2154*4104d13fSDmitry Torokhov 			break;
2155*4104d13fSDmitry Torokhov 		}
2156*4104d13fSDmitry Torokhov 	}
2157*4104d13fSDmitry Torokhov 
2158*4104d13fSDmitry Torokhov 	/* Associate this driver's struct with the usb interface.
2159*4104d13fSDmitry Torokhov 	 */
2160*4104d13fSDmitry Torokhov 	usb_set_intfdata(intf, aiptek);
2161*4104d13fSDmitry Torokhov 
2162*4104d13fSDmitry Torokhov 	/* Set up the sysfs files
2163*4104d13fSDmitry Torokhov 	 */
2164*4104d13fSDmitry Torokhov 	aiptek_add_files(&intf->dev);
2165*4104d13fSDmitry Torokhov 
2166*4104d13fSDmitry Torokhov 	/* Make sure the evdev module is loaded. Assuming evdev IS a module :-)
2167*4104d13fSDmitry Torokhov 	 */
2168*4104d13fSDmitry Torokhov 	if (request_module("evdev") != 0)
2169*4104d13fSDmitry Torokhov 		info("aiptek: error loading 'evdev' module");
2170*4104d13fSDmitry Torokhov 
2171*4104d13fSDmitry Torokhov 	return 0;
2172*4104d13fSDmitry Torokhov 
2173*4104d13fSDmitry Torokhov  fail2:	usb_buffer_free(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data,
2174*4104d13fSDmitry Torokhov 			aiptek->data_dma);
2175*4104d13fSDmitry Torokhov  fail1:	input_free_device(inputdev);
2176*4104d13fSDmitry Torokhov 	kfree(aiptek);
2177*4104d13fSDmitry Torokhov 	return err;
2178*4104d13fSDmitry Torokhov }
2179*4104d13fSDmitry Torokhov 
2180*4104d13fSDmitry Torokhov /***********************************************************************
2181*4104d13fSDmitry Torokhov  * Deal with tablet disconnecting from the system.
2182*4104d13fSDmitry Torokhov  */
2183*4104d13fSDmitry Torokhov static void aiptek_disconnect(struct usb_interface *intf)
2184*4104d13fSDmitry Torokhov {
2185*4104d13fSDmitry Torokhov 	struct aiptek *aiptek = usb_get_intfdata(intf);
2186*4104d13fSDmitry Torokhov 
2187*4104d13fSDmitry Torokhov 	/* Disassociate driver's struct with usb interface
2188*4104d13fSDmitry Torokhov 	 */
2189*4104d13fSDmitry Torokhov 	usb_set_intfdata(intf, NULL);
2190*4104d13fSDmitry Torokhov 	if (aiptek != NULL) {
2191*4104d13fSDmitry Torokhov 		/* Free & unhook everything from the system.
2192*4104d13fSDmitry Torokhov 		 */
2193*4104d13fSDmitry Torokhov 		usb_kill_urb(aiptek->urb);
2194*4104d13fSDmitry Torokhov 		input_unregister_device(aiptek->inputdev);
2195*4104d13fSDmitry Torokhov 		aiptek_delete_files(&intf->dev);
2196*4104d13fSDmitry Torokhov 		usb_free_urb(aiptek->urb);
2197*4104d13fSDmitry Torokhov 		usb_buffer_free(interface_to_usbdev(intf),
2198*4104d13fSDmitry Torokhov 				AIPTEK_PACKET_LENGTH,
2199*4104d13fSDmitry Torokhov 				aiptek->data, aiptek->data_dma);
2200*4104d13fSDmitry Torokhov 		kfree(aiptek);
2201*4104d13fSDmitry Torokhov 	}
2202*4104d13fSDmitry Torokhov }
2203*4104d13fSDmitry Torokhov 
2204*4104d13fSDmitry Torokhov static struct usb_driver aiptek_driver = {
2205*4104d13fSDmitry Torokhov 	.name = "aiptek",
2206*4104d13fSDmitry Torokhov 	.probe = aiptek_probe,
2207*4104d13fSDmitry Torokhov 	.disconnect = aiptek_disconnect,
2208*4104d13fSDmitry Torokhov 	.id_table = aiptek_ids,
2209*4104d13fSDmitry Torokhov };
2210*4104d13fSDmitry Torokhov 
2211*4104d13fSDmitry Torokhov static int __init aiptek_init(void)
2212*4104d13fSDmitry Torokhov {
2213*4104d13fSDmitry Torokhov 	int result = usb_register(&aiptek_driver);
2214*4104d13fSDmitry Torokhov 	if (result == 0) {
2215*4104d13fSDmitry Torokhov 		info(DRIVER_VERSION ": " DRIVER_AUTHOR);
2216*4104d13fSDmitry Torokhov 		info(DRIVER_DESC);
2217*4104d13fSDmitry Torokhov 	}
2218*4104d13fSDmitry Torokhov 	return result;
2219*4104d13fSDmitry Torokhov }
2220*4104d13fSDmitry Torokhov 
2221*4104d13fSDmitry Torokhov static void __exit aiptek_exit(void)
2222*4104d13fSDmitry Torokhov {
2223*4104d13fSDmitry Torokhov 	usb_deregister(&aiptek_driver);
2224*4104d13fSDmitry Torokhov }
2225*4104d13fSDmitry Torokhov 
2226*4104d13fSDmitry Torokhov MODULE_AUTHOR(DRIVER_AUTHOR);
2227*4104d13fSDmitry Torokhov MODULE_DESCRIPTION(DRIVER_DESC);
2228*4104d13fSDmitry Torokhov MODULE_LICENSE("GPL");
2229*4104d13fSDmitry Torokhov 
2230*4104d13fSDmitry Torokhov module_param(programmableDelay, int, 0);
2231*4104d13fSDmitry Torokhov MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming");
2232*4104d13fSDmitry Torokhov module_param(jitterDelay, int, 0);
2233*4104d13fSDmitry Torokhov MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay");
2234*4104d13fSDmitry Torokhov 
2235*4104d13fSDmitry Torokhov module_init(aiptek_init);
2236*4104d13fSDmitry Torokhov module_exit(aiptek_exit);
2237