14104d13fSDmitry Torokhov /* 24104d13fSDmitry Torokhov * Native support for the Aiptek HyperPen USB Tablets 34104d13fSDmitry Torokhov * (4000U/5000U/6000U/8000U/12000U) 44104d13fSDmitry Torokhov * 54104d13fSDmitry Torokhov * Copyright (c) 2001 Chris Atenasio <chris@crud.net> 64104d13fSDmitry Torokhov * Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net> 74104d13fSDmitry Torokhov * 84104d13fSDmitry Torokhov * based on wacom.c by 94104d13fSDmitry Torokhov * Vojtech Pavlik <vojtech@suse.cz> 104104d13fSDmitry Torokhov * Andreas Bach Aaen <abach@stofanet.dk> 114104d13fSDmitry Torokhov * Clifford Wolf <clifford@clifford.at> 124104d13fSDmitry Torokhov * Sam Mosel <sam.mosel@computer.org> 134104d13fSDmitry Torokhov * James E. Blair <corvus@gnu.org> 144104d13fSDmitry Torokhov * Daniel Egger <egger@suse.de> 154104d13fSDmitry Torokhov * 164104d13fSDmitry Torokhov * Many thanks to Oliver Kuechemann for his support. 174104d13fSDmitry Torokhov * 184104d13fSDmitry Torokhov * ChangeLog: 194104d13fSDmitry Torokhov * v0.1 - Initial release 204104d13fSDmitry Torokhov * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley) 214104d13fSDmitry Torokhov * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002) 224104d13fSDmitry Torokhov * Released to Linux 2.4.19 and 2.5.x 234104d13fSDmitry Torokhov * v0.4 - Rewrote substantial portions of the code to deal with 244104d13fSDmitry Torokhov * corrected control sequences, timing, dynamic configuration, 254104d13fSDmitry Torokhov * support of 6000U - 12000U, procfs, and macro key support 264104d13fSDmitry Torokhov * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley) 274104d13fSDmitry Torokhov * v1.0 - Added support for diagnostic messages, count of messages 284104d13fSDmitry Torokhov * received from URB - Mar-8-2003, Bryan W. Headley 294104d13fSDmitry Torokhov * v1.1 - added support for tablet resolution, changed DV and proximity 304104d13fSDmitry Torokhov * some corrections - Jun-22-2003, martin schneebacher 314104d13fSDmitry Torokhov * - Added support for the sysfs interface, deprecating the 324104d13fSDmitry Torokhov * procfs interface for 2.5.x kernel. Also added support for 334104d13fSDmitry Torokhov * Wheel command. Bryan W. Headley July-15-2003. 344104d13fSDmitry Torokhov * v1.2 - Reworked jitter timer as a kernel thread. 354104d13fSDmitry Torokhov * Bryan W. Headley November-28-2003/Jan-10-2004. 364104d13fSDmitry Torokhov * v1.3 - Repaired issue of kernel thread going nuts on single-processor 374104d13fSDmitry Torokhov * machines, introduced programmableDelay as a command line 384104d13fSDmitry Torokhov * parameter. Feb 7 2004, Bryan W. Headley. 394104d13fSDmitry Torokhov * v1.4 - Re-wire jitter so it does not require a thread. Courtesy of 404104d13fSDmitry Torokhov * Rene van Paassen. Added reporting of physical pointer device 414104d13fSDmitry Torokhov * (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know 424104d13fSDmitry Torokhov * for reports 1, 6.) 434104d13fSDmitry Torokhov * what physical device reports for reports 1, 6.) Also enabled 444104d13fSDmitry Torokhov * MOUSE and LENS tool button modes. Renamed "rubber" to "eraser". 454104d13fSDmitry Torokhov * Feb 20, 2004, Bryan W. Headley. 464104d13fSDmitry Torokhov * v1.5 - Added previousJitterable, so we don't do jitter delay when the 474104d13fSDmitry Torokhov * user is holding a button down for periods of time. 484104d13fSDmitry Torokhov * 494104d13fSDmitry Torokhov * NOTE: 504104d13fSDmitry Torokhov * This kernel driver is augmented by the "Aiptek" XFree86 input 514104d13fSDmitry Torokhov * driver for your X server, as well as the Gaiptek GUI Front-end 524104d13fSDmitry Torokhov * "Tablet Manager". 534104d13fSDmitry Torokhov * These three products are highly interactive with one another, 544104d13fSDmitry Torokhov * so therefore it's easier to document them all as one subsystem. 554104d13fSDmitry Torokhov * Please visit the project's "home page", located at, 564104d13fSDmitry Torokhov * http://aiptektablet.sourceforge.net. 574104d13fSDmitry Torokhov * 584104d13fSDmitry Torokhov * This program is free software; you can redistribute it and/or modify 594104d13fSDmitry Torokhov * it under the terms of the GNU General Public License as published by 604104d13fSDmitry Torokhov * the Free Software Foundation; either version 2 of the License, or 614104d13fSDmitry Torokhov * (at your option) any later version. 624104d13fSDmitry Torokhov * 634104d13fSDmitry Torokhov * This program is distributed in the hope that it will be useful, 644104d13fSDmitry Torokhov * but WITHOUT ANY WARRANTY; without even the implied warranty of 654104d13fSDmitry Torokhov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 664104d13fSDmitry Torokhov * GNU General Public License for more details. 674104d13fSDmitry Torokhov * 684104d13fSDmitry Torokhov * You should have received a copy of the GNU General Public License 694104d13fSDmitry Torokhov * along with this program; if not, write to the Free Software 704104d13fSDmitry Torokhov * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 714104d13fSDmitry Torokhov */ 724104d13fSDmitry Torokhov 734104d13fSDmitry Torokhov #include <linux/jiffies.h> 744104d13fSDmitry Torokhov #include <linux/kernel.h> 754104d13fSDmitry Torokhov #include <linux/slab.h> 764104d13fSDmitry Torokhov #include <linux/module.h> 774104d13fSDmitry Torokhov #include <linux/init.h> 784104d13fSDmitry Torokhov #include <linux/usb/input.h> 794104d13fSDmitry Torokhov #include <asm/uaccess.h> 804104d13fSDmitry Torokhov #include <asm/unaligned.h> 814104d13fSDmitry Torokhov 824104d13fSDmitry Torokhov /* 834104d13fSDmitry Torokhov * Version Information 844104d13fSDmitry Torokhov */ 855035522dSRene van Paassen #define DRIVER_VERSION "v2.3 (May 2, 2007)" 865035522dSRene van Paassen #define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen" 874104d13fSDmitry Torokhov #define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)" 884104d13fSDmitry Torokhov 894104d13fSDmitry Torokhov /* 904104d13fSDmitry Torokhov * Aiptek status packet: 914104d13fSDmitry Torokhov * 924104d13fSDmitry Torokhov * (returned as Report 1 - relative coordinates from mouse and stylus) 934104d13fSDmitry Torokhov * 944104d13fSDmitry Torokhov * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 954104d13fSDmitry Torokhov * byte0 0 0 0 0 0 0 0 1 964104d13fSDmitry Torokhov * byte1 0 0 0 0 0 BS2 BS Tip 974104d13fSDmitry Torokhov * byte2 X7 X6 X5 X4 X3 X2 X1 X0 984104d13fSDmitry Torokhov * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 994104d13fSDmitry Torokhov * 1004104d13fSDmitry Torokhov * (returned as Report 2 - absolute coordinates from the stylus) 1014104d13fSDmitry Torokhov * 1024104d13fSDmitry Torokhov * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 1034104d13fSDmitry Torokhov * byte0 0 0 0 0 0 0 1 0 1044104d13fSDmitry Torokhov * byte1 X7 X6 X5 X4 X3 X2 X1 X0 1054104d13fSDmitry Torokhov * byte2 X15 X14 X13 X12 X11 X10 X9 X8 1064104d13fSDmitry Torokhov * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 1074104d13fSDmitry Torokhov * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 1084104d13fSDmitry Torokhov * byte5 * * * BS2 BS1 Tip IR DV 1094104d13fSDmitry Torokhov * byte6 P7 P6 P5 P4 P3 P2 P1 P0 1104104d13fSDmitry Torokhov * byte7 P15 P14 P13 P12 P11 P10 P9 P8 1114104d13fSDmitry Torokhov * 1124104d13fSDmitry Torokhov * (returned as Report 3 - absolute coordinates from the mouse) 1134104d13fSDmitry Torokhov * 1144104d13fSDmitry Torokhov * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 1152fe57416SRene van Paassen * byte0 0 0 0 0 0 0 1 1 1164104d13fSDmitry Torokhov * byte1 X7 X6 X5 X4 X3 X2 X1 X0 1174104d13fSDmitry Torokhov * byte2 X15 X14 X13 X12 X11 X10 X9 X8 1184104d13fSDmitry Torokhov * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 1194104d13fSDmitry Torokhov * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 1204104d13fSDmitry Torokhov * byte5 * * * BS2 BS1 Tip IR DV 1214104d13fSDmitry Torokhov * byte6 P7 P6 P5 P4 P3 P2 P1 P0 1224104d13fSDmitry Torokhov * byte7 P15 P14 P13 P12 P11 P10 P9 P8 1234104d13fSDmitry Torokhov * 1244104d13fSDmitry Torokhov * (returned as Report 4 - macrokeys from the stylus) 1254104d13fSDmitry Torokhov * 1264104d13fSDmitry Torokhov * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 1274104d13fSDmitry Torokhov * byte0 0 0 0 0 0 1 0 0 1284104d13fSDmitry Torokhov * byte1 0 0 0 BS2 BS Tip IR DV 1294104d13fSDmitry Torokhov * byte2 0 0 0 0 0 0 1 0 1304104d13fSDmitry Torokhov * byte3 0 0 0 K4 K3 K2 K1 K0 1314104d13fSDmitry Torokhov * byte4 P7 P6 P5 P4 P3 P2 P1 P0 1324104d13fSDmitry Torokhov * byte5 P15 P14 P13 P12 P11 P10 P9 P8 1334104d13fSDmitry Torokhov * 1344104d13fSDmitry Torokhov * (returned as Report 5 - macrokeys from the mouse) 1354104d13fSDmitry Torokhov * 1364104d13fSDmitry Torokhov * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 1372fe57416SRene van Paassen * byte0 0 0 0 0 0 1 0 1 1384104d13fSDmitry Torokhov * byte1 0 0 0 BS2 BS Tip IR DV 1394104d13fSDmitry Torokhov * byte2 0 0 0 0 0 0 1 0 1404104d13fSDmitry Torokhov * byte3 0 0 0 K4 K3 K2 K1 K0 1414104d13fSDmitry Torokhov * byte4 P7 P6 P5 P4 P3 P2 P1 P0 1424104d13fSDmitry Torokhov * byte5 P15 P14 P13 P12 P11 P10 P9 P8 1434104d13fSDmitry Torokhov * 1444104d13fSDmitry Torokhov * IR: In Range = Proximity on 1454104d13fSDmitry Torokhov * DV = Data Valid 1464104d13fSDmitry Torokhov * BS = Barrel Switch (as in, macro keys) 1474104d13fSDmitry Torokhov * BS2 also referred to as Tablet Pick 1484104d13fSDmitry Torokhov * 1494104d13fSDmitry Torokhov * Command Summary: 1504104d13fSDmitry Torokhov * 1514104d13fSDmitry Torokhov * Use report_type CONTROL (3) 1524104d13fSDmitry Torokhov * Use report_id 2 1534104d13fSDmitry Torokhov * 1544104d13fSDmitry Torokhov * Command/Data Description Return Bytes Return Value 1554104d13fSDmitry Torokhov * 0x10/0x00 SwitchToMouse 0 1564104d13fSDmitry Torokhov * 0x10/0x01 SwitchToTablet 0 1574104d13fSDmitry Torokhov * 0x18/0x04 SetResolution 0 1584104d13fSDmitry Torokhov * 0x12/0xFF AutoGainOn 0 1594104d13fSDmitry Torokhov * 0x17/0x00 FilterOn 0 1604104d13fSDmitry Torokhov * 0x01/0x00 GetXExtension 2 MaxX 1614104d13fSDmitry Torokhov * 0x01/0x01 GetYExtension 2 MaxY 1624104d13fSDmitry Torokhov * 0x02/0x00 GetModelCode 2 ModelCode = LOBYTE 1634104d13fSDmitry Torokhov * 0x03/0x00 GetODMCode 2 ODMCode 1644104d13fSDmitry Torokhov * 0x08/0x00 GetPressureLevels 2 =512 1654104d13fSDmitry Torokhov * 0x04/0x00 GetFirmwareVersion 2 Firmware Version 1664104d13fSDmitry Torokhov * 0x11/0x02 EnableMacroKeys 0 1674104d13fSDmitry Torokhov * 1684104d13fSDmitry Torokhov * To initialize the tablet: 1694104d13fSDmitry Torokhov * 1704104d13fSDmitry Torokhov * (1) Send Resolution500LPI (Command) 1714104d13fSDmitry Torokhov * (2) Query for Model code (Option Report) 1724104d13fSDmitry Torokhov * (3) Query for ODM code (Option Report) 1734104d13fSDmitry Torokhov * (4) Query for firmware (Option Report) 1744104d13fSDmitry Torokhov * (5) Query for GetXExtension (Option Report) 1754104d13fSDmitry Torokhov * (6) Query for GetYExtension (Option Report) 1764104d13fSDmitry Torokhov * (7) Query for GetPressureLevels (Option Report) 1774104d13fSDmitry Torokhov * (8) SwitchToTablet for Absolute coordinates, or 1784104d13fSDmitry Torokhov * SwitchToMouse for Relative coordinates (Command) 1794104d13fSDmitry Torokhov * (9) EnableMacroKeys (Command) 1804104d13fSDmitry Torokhov * (10) FilterOn (Command) 1814104d13fSDmitry Torokhov * (11) AutoGainOn (Command) 1824104d13fSDmitry Torokhov * 1834104d13fSDmitry Torokhov * (Step 9 can be omitted, but you'll then have no function keys.) 1844104d13fSDmitry Torokhov */ 1854104d13fSDmitry Torokhov 1864104d13fSDmitry Torokhov #define USB_VENDOR_ID_AIPTEK 0x08ca 187a32bcc45SGuryanov Dmitry #define USB_VENDOR_ID_KYE 0x0458 1884104d13fSDmitry Torokhov #define USB_REQ_GET_REPORT 0x01 1894104d13fSDmitry Torokhov #define USB_REQ_SET_REPORT 0x09 1904104d13fSDmitry Torokhov 1914104d13fSDmitry Torokhov /* PointerMode codes 1924104d13fSDmitry Torokhov */ 1934104d13fSDmitry Torokhov #define AIPTEK_POINTER_ONLY_MOUSE_MODE 0 1944104d13fSDmitry Torokhov #define AIPTEK_POINTER_ONLY_STYLUS_MODE 1 1954104d13fSDmitry Torokhov #define AIPTEK_POINTER_EITHER_MODE 2 1964104d13fSDmitry Torokhov 1974104d13fSDmitry Torokhov #define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \ 1984104d13fSDmitry Torokhov (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \ 1994104d13fSDmitry Torokhov a == AIPTEK_POINTER_EITHER_MODE) 2004104d13fSDmitry Torokhov #define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \ 2014104d13fSDmitry Torokhov (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \ 2024104d13fSDmitry Torokhov a == AIPTEK_POINTER_EITHER_MODE) 2034104d13fSDmitry Torokhov 2044104d13fSDmitry Torokhov /* CoordinateMode code 2054104d13fSDmitry Torokhov */ 2064104d13fSDmitry Torokhov #define AIPTEK_COORDINATE_RELATIVE_MODE 0 2074104d13fSDmitry Torokhov #define AIPTEK_COORDINATE_ABSOLUTE_MODE 1 2084104d13fSDmitry Torokhov 2094104d13fSDmitry Torokhov /* XTilt and YTilt values 2104104d13fSDmitry Torokhov */ 2114104d13fSDmitry Torokhov #define AIPTEK_TILT_MIN (-128) 2124104d13fSDmitry Torokhov #define AIPTEK_TILT_MAX 127 2134104d13fSDmitry Torokhov #define AIPTEK_TILT_DISABLE (-10101) 2144104d13fSDmitry Torokhov 2154104d13fSDmitry Torokhov /* Wheel values 2164104d13fSDmitry Torokhov */ 2174104d13fSDmitry Torokhov #define AIPTEK_WHEEL_MIN 0 2184104d13fSDmitry Torokhov #define AIPTEK_WHEEL_MAX 1024 2194104d13fSDmitry Torokhov #define AIPTEK_WHEEL_DISABLE (-10101) 2204104d13fSDmitry Torokhov 2214104d13fSDmitry Torokhov /* ToolCode values, which BTW are 0x140 .. 0x14f 222b3b6cf1dSRene van Paassen * We have things set up such that if the tool button has changed, 223b3b6cf1dSRene van Paassen * the tools get reset. 2244104d13fSDmitry Torokhov */ 2254104d13fSDmitry Torokhov /* toolMode codes 2264104d13fSDmitry Torokhov */ 2274104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN 2284104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN 2294104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL 2304104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH 2314104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH 2324104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_ERASER_MODE BTN_TOOL_RUBBER 2334104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_MOUSE_MODE BTN_TOOL_MOUSE 2344104d13fSDmitry Torokhov #define AIPTEK_TOOL_BUTTON_LENS_MODE BTN_TOOL_LENS 2354104d13fSDmitry Torokhov 2364104d13fSDmitry Torokhov /* Diagnostic message codes 2374104d13fSDmitry Torokhov */ 2384104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_NA 0 2394104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1 2404104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 2414104d13fSDmitry Torokhov #define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 2424104d13fSDmitry Torokhov 2434104d13fSDmitry Torokhov /* Time to wait (in ms) to help mask hand jittering 2444104d13fSDmitry Torokhov * when pressing the stylus buttons. 2454104d13fSDmitry Torokhov */ 2464104d13fSDmitry Torokhov #define AIPTEK_JITTER_DELAY_DEFAULT 50 2474104d13fSDmitry Torokhov 2484104d13fSDmitry Torokhov /* Time to wait (in ms) in-between sending the tablet 2494104d13fSDmitry Torokhov * a command and beginning the process of reading the return 2504104d13fSDmitry Torokhov * sequence from the tablet. 2514104d13fSDmitry Torokhov */ 2524104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_25 25 2534104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_50 50 2544104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_100 100 2554104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_200 200 2564104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_300 300 2574104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_400 400 2584104d13fSDmitry Torokhov #define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT AIPTEK_PROGRAMMABLE_DELAY_400 2594104d13fSDmitry Torokhov 2604104d13fSDmitry Torokhov /* Mouse button programming 2614104d13fSDmitry Torokhov */ 262ce0982edSRene van Paassen #define AIPTEK_MOUSE_LEFT_BUTTON 0x04 263ce0982edSRene van Paassen #define AIPTEK_MOUSE_RIGHT_BUTTON 0x08 264ce0982edSRene van Paassen #define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10 2654104d13fSDmitry Torokhov 2664104d13fSDmitry Torokhov /* Stylus button programming 2674104d13fSDmitry Torokhov */ 2684104d13fSDmitry Torokhov #define AIPTEK_STYLUS_LOWER_BUTTON 0x08 2694104d13fSDmitry Torokhov #define AIPTEK_STYLUS_UPPER_BUTTON 0x10 2704104d13fSDmitry Torokhov 2714104d13fSDmitry Torokhov /* Length of incoming packet from the tablet 2724104d13fSDmitry Torokhov */ 2734104d13fSDmitry Torokhov #define AIPTEK_PACKET_LENGTH 8 2744104d13fSDmitry Torokhov 2754104d13fSDmitry Torokhov /* We report in EV_MISC both the proximity and 2764104d13fSDmitry Torokhov * whether the report came from the stylus, tablet mouse 2774104d13fSDmitry Torokhov * or "unknown" -- Unknown when the tablet is in relative 2784104d13fSDmitry Torokhov * mode, because we only get report 1's. 2794104d13fSDmitry Torokhov */ 2804104d13fSDmitry Torokhov #define AIPTEK_REPORT_TOOL_UNKNOWN 0x10 2814104d13fSDmitry Torokhov #define AIPTEK_REPORT_TOOL_STYLUS 0x20 2824104d13fSDmitry Torokhov #define AIPTEK_REPORT_TOOL_MOUSE 0x40 2834104d13fSDmitry Torokhov 2844104d13fSDmitry Torokhov static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT; 2854104d13fSDmitry Torokhov static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT; 2864104d13fSDmitry Torokhov 2874104d13fSDmitry Torokhov struct aiptek_features { 2884104d13fSDmitry Torokhov int odmCode; /* Tablet manufacturer code */ 2894104d13fSDmitry Torokhov int modelCode; /* Tablet model code (not unique) */ 2904104d13fSDmitry Torokhov int firmwareCode; /* prom/eeprom version */ 2914104d13fSDmitry Torokhov char usbPath[64 + 1]; /* device's physical usb path */ 2924104d13fSDmitry Torokhov }; 2934104d13fSDmitry Torokhov 2944104d13fSDmitry Torokhov struct aiptek_settings { 2954104d13fSDmitry Torokhov int pointerMode; /* stylus-, mouse-only or either */ 2964104d13fSDmitry Torokhov int coordinateMode; /* absolute/relative coords */ 2974104d13fSDmitry Torokhov int toolMode; /* pen, pencil, brush, etc. tool */ 2984104d13fSDmitry Torokhov int xTilt; /* synthetic xTilt amount */ 2994104d13fSDmitry Torokhov int yTilt; /* synthetic yTilt amount */ 3004104d13fSDmitry Torokhov int wheel; /* synthetic wheel amount */ 3014104d13fSDmitry Torokhov int stylusButtonUpper; /* stylus upper btn delivers... */ 3024104d13fSDmitry Torokhov int stylusButtonLower; /* stylus lower btn delivers... */ 3034104d13fSDmitry Torokhov int mouseButtonLeft; /* mouse left btn delivers... */ 3044104d13fSDmitry Torokhov int mouseButtonMiddle; /* mouse middle btn delivers... */ 3054104d13fSDmitry Torokhov int mouseButtonRight; /* mouse right btn delivers... */ 3064104d13fSDmitry Torokhov int programmableDelay; /* delay for tablet programming */ 3074104d13fSDmitry Torokhov int jitterDelay; /* delay for hand jittering */ 3084104d13fSDmitry Torokhov }; 3094104d13fSDmitry Torokhov 3104104d13fSDmitry Torokhov struct aiptek { 3114104d13fSDmitry Torokhov struct input_dev *inputdev; /* input device struct */ 3124104d13fSDmitry Torokhov struct usb_device *usbdev; /* usb device struct */ 3134104d13fSDmitry Torokhov struct urb *urb; /* urb for incoming reports */ 3144104d13fSDmitry Torokhov dma_addr_t data_dma; /* our dma stuffage */ 3154104d13fSDmitry Torokhov struct aiptek_features features; /* tablet's array of features */ 3164104d13fSDmitry Torokhov struct aiptek_settings curSetting; /* tablet's current programmable */ 3174104d13fSDmitry Torokhov struct aiptek_settings newSetting; /* ... and new param settings */ 3184104d13fSDmitry Torokhov unsigned int ifnum; /* interface number for IO */ 3194104d13fSDmitry Torokhov int diagnostic; /* tablet diagnostic codes */ 3204104d13fSDmitry Torokhov unsigned long eventCount; /* event count */ 3214104d13fSDmitry Torokhov int inDelay; /* jitter: in jitter delay? */ 3224104d13fSDmitry Torokhov unsigned long endDelay; /* jitter: time when delay ends */ 3234104d13fSDmitry Torokhov int previousJitterable; /* jitterable prev value */ 324b3b6cf1dSRene van Paassen 325b3b6cf1dSRene van Paassen int lastMacro; /* macro key to reset */ 326b3b6cf1dSRene van Paassen int previousToolMode; /* pen, pencil, brush, etc. tool */ 3274104d13fSDmitry Torokhov unsigned char *data; /* incoming packet data */ 3284104d13fSDmitry Torokhov }; 3294104d13fSDmitry Torokhov 3301a54f49eSRene van Paassen static const int eventTypes[] = { 3311a54f49eSRene van Paassen EV_KEY, EV_ABS, EV_REL, EV_MSC, 3321a54f49eSRene van Paassen }; 3331a54f49eSRene van Paassen 3341a54f49eSRene van Paassen static const int absEvents[] = { 3351a54f49eSRene van Paassen ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, 3361a54f49eSRene van Paassen ABS_WHEEL, ABS_MISC, 3371a54f49eSRene van Paassen }; 3381a54f49eSRene van Paassen 3391a54f49eSRene van Paassen static const int relEvents[] = { 3401a54f49eSRene van Paassen REL_X, REL_Y, REL_WHEEL, 3411a54f49eSRene van Paassen }; 3421a54f49eSRene van Paassen 34333936fa6SDmitry Torokhov static const int buttonEvents[] = { 34433936fa6SDmitry Torokhov BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, 34533936fa6SDmitry Torokhov BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, 34633936fa6SDmitry Torokhov BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH, 34733936fa6SDmitry Torokhov BTN_STYLUS, BTN_STYLUS2, 34833936fa6SDmitry Torokhov }; 34933936fa6SDmitry Torokhov 3504104d13fSDmitry Torokhov /* 3514104d13fSDmitry Torokhov * Permit easy lookup of keyboard events to send, versus 3524104d13fSDmitry Torokhov * the bitmap which comes from the tablet. This hides the 3534104d13fSDmitry Torokhov * issue that the F_keys are not sequentially numbered. 3544104d13fSDmitry Torokhov */ 3554104d13fSDmitry Torokhov static const int macroKeyEvents[] = { 3564104d13fSDmitry Torokhov KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, 3574104d13fSDmitry Torokhov KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, 3584104d13fSDmitry Torokhov KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, 3594104d13fSDmitry Torokhov KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, 3604104d13fSDmitry Torokhov KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, 3614104d13fSDmitry Torokhov KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0 3624104d13fSDmitry Torokhov }; 3634104d13fSDmitry Torokhov 3644104d13fSDmitry Torokhov /*********************************************************************** 365c9404c9cSAdam Buchbinder * Map values to strings and back. Every map should have the following 366cd438a58SDmitry Torokhov * as its last element: { NULL, AIPTEK_INVALID_VALUE }. 367cd438a58SDmitry Torokhov */ 368cd438a58SDmitry Torokhov #define AIPTEK_INVALID_VALUE -1 369cd438a58SDmitry Torokhov 370cd438a58SDmitry Torokhov struct aiptek_map { 371cd438a58SDmitry Torokhov const char *string; 372cd438a58SDmitry Torokhov int value; 373cd438a58SDmitry Torokhov }; 374cd438a58SDmitry Torokhov 375cd438a58SDmitry Torokhov static int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count) 376cd438a58SDmitry Torokhov { 377cd438a58SDmitry Torokhov const struct aiptek_map *p; 378cd438a58SDmitry Torokhov 3790112db36SRene van Paassen if (str[count - 1] == '\n') 3800112db36SRene van Paassen count--; 3810112db36SRene van Paassen 382cd438a58SDmitry Torokhov for (p = map; p->string; p++) 383cd438a58SDmitry Torokhov if (!strncmp(str, p->string, count)) 384cd438a58SDmitry Torokhov return p->value; 385cd438a58SDmitry Torokhov 386cd438a58SDmitry Torokhov return AIPTEK_INVALID_VALUE; 387cd438a58SDmitry Torokhov } 388cd438a58SDmitry Torokhov 389cd438a58SDmitry Torokhov static const char *map_val_to_str(const struct aiptek_map *map, int val) 390cd438a58SDmitry Torokhov { 391cd438a58SDmitry Torokhov const struct aiptek_map *p; 392cd438a58SDmitry Torokhov 393cd438a58SDmitry Torokhov for (p = map; p->value != AIPTEK_INVALID_VALUE; p++) 394cd438a58SDmitry Torokhov if (val == p->value) 395cd438a58SDmitry Torokhov return p->string; 396cd438a58SDmitry Torokhov 397cd438a58SDmitry Torokhov return "unknown"; 398cd438a58SDmitry Torokhov } 399cd438a58SDmitry Torokhov 400cd438a58SDmitry Torokhov /*********************************************************************** 4014104d13fSDmitry Torokhov * aiptek_irq can receive one of six potential reports. 4024104d13fSDmitry Torokhov * The documentation for each is in the body of the function. 4034104d13fSDmitry Torokhov * 4044104d13fSDmitry Torokhov * The tablet reports on several attributes per invocation of 4054104d13fSDmitry Torokhov * aiptek_irq. Because the Linux Input Event system allows the 4064104d13fSDmitry Torokhov * transmission of ONE attribute per input_report_xxx() call, 4074104d13fSDmitry Torokhov * collation has to be done on the other end to reconstitute 4084104d13fSDmitry Torokhov * a complete tablet report. Further, the number of Input Event reports 4094104d13fSDmitry Torokhov * submitted varies, depending on what USB report type, and circumstance. 4104104d13fSDmitry Torokhov * To deal with this, EV_MSC is used to indicate an 'end-of-report' 4114104d13fSDmitry Torokhov * message. This has been an undocumented convention understood by the kernel 4124104d13fSDmitry Torokhov * tablet driver and clients such as gpm and XFree86's tablet drivers. 4134104d13fSDmitry Torokhov * 4144104d13fSDmitry Torokhov * Of the information received from the tablet, the one piece I 4154104d13fSDmitry Torokhov * cannot transmit is the proximity bit (without resorting to an EV_MSC 4164104d13fSDmitry Torokhov * convention above.) I therefore have taken over REL_MISC and ABS_MISC 4174104d13fSDmitry Torokhov * (for relative and absolute reports, respectively) for communicating 4184104d13fSDmitry Torokhov * Proximity. Why two events? I thought it interesting to know if the 4194104d13fSDmitry Torokhov * Proximity event occurred while the tablet was in absolute or relative 4204104d13fSDmitry Torokhov * mode. 421da9fda43SRene van Paassen * Update: REL_MISC proved not to be such a good idea. With REL_MISC you 422da9fda43SRene van Paassen * get an event transmitted each time. ABS_MISC works better, since it 423da9fda43SRene van Paassen * can be set and re-set. Thus, only using ABS_MISC from now on. 4244104d13fSDmitry Torokhov * 4254104d13fSDmitry Torokhov * Other tablets use the notion of a certain minimum stylus pressure 4264104d13fSDmitry Torokhov * to infer proximity. While that could have been done, that is yet 4274104d13fSDmitry Torokhov * another 'by convention' behavior, the documentation for which 4284104d13fSDmitry Torokhov * would be spread between two (or more) pieces of software. 4294104d13fSDmitry Torokhov * 4304104d13fSDmitry Torokhov * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and 4314104d13fSDmitry Torokhov * replaced with the input_sync() method (which emits EV_SYN.) 4324104d13fSDmitry Torokhov */ 4334104d13fSDmitry Torokhov 4344104d13fSDmitry Torokhov static void aiptek_irq(struct urb *urb) 4354104d13fSDmitry Torokhov { 4364104d13fSDmitry Torokhov struct aiptek *aiptek = urb->context; 4374104d13fSDmitry Torokhov unsigned char *data = aiptek->data; 4384104d13fSDmitry Torokhov struct input_dev *inputdev = aiptek->inputdev; 4394104d13fSDmitry Torokhov int jitterable = 0; 4404104d13fSDmitry Torokhov int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck; 4414104d13fSDmitry Torokhov 4424104d13fSDmitry Torokhov switch (urb->status) { 4434104d13fSDmitry Torokhov case 0: 4444104d13fSDmitry Torokhov /* Success */ 4454104d13fSDmitry Torokhov break; 4464104d13fSDmitry Torokhov 4474104d13fSDmitry Torokhov case -ECONNRESET: 4484104d13fSDmitry Torokhov case -ENOENT: 4494104d13fSDmitry Torokhov case -ESHUTDOWN: 4504104d13fSDmitry Torokhov /* This urb is terminated, clean up */ 4514104d13fSDmitry Torokhov dbg("%s - urb shutting down with status: %d", 452ea3e6c59SHarvey Harrison __func__, urb->status); 4534104d13fSDmitry Torokhov return; 4544104d13fSDmitry Torokhov 4554104d13fSDmitry Torokhov default: 4564104d13fSDmitry Torokhov dbg("%s - nonzero urb status received: %d", 457ea3e6c59SHarvey Harrison __func__, urb->status); 4584104d13fSDmitry Torokhov goto exit; 4594104d13fSDmitry Torokhov } 4604104d13fSDmitry Torokhov 4614104d13fSDmitry Torokhov /* See if we are in a delay loop -- throw out report if true. 4624104d13fSDmitry Torokhov */ 4634104d13fSDmitry Torokhov if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) { 4644104d13fSDmitry Torokhov goto exit; 4654104d13fSDmitry Torokhov } 4664104d13fSDmitry Torokhov 4674104d13fSDmitry Torokhov aiptek->inDelay = 0; 4684104d13fSDmitry Torokhov aiptek->eventCount++; 4694104d13fSDmitry Torokhov 4704104d13fSDmitry Torokhov /* Report 1 delivers relative coordinates with either a stylus 4714104d13fSDmitry Torokhov * or the mouse. You do not know, however, which input 4724104d13fSDmitry Torokhov * tool generated the event. 4734104d13fSDmitry Torokhov */ 4744104d13fSDmitry Torokhov if (data[0] == 1) { 4754104d13fSDmitry Torokhov if (aiptek->curSetting.coordinateMode == 4764104d13fSDmitry Torokhov AIPTEK_COORDINATE_ABSOLUTE_MODE) { 4774104d13fSDmitry Torokhov aiptek->diagnostic = 4784104d13fSDmitry Torokhov AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE; 4794104d13fSDmitry Torokhov } else { 48037767b66SDmitry Torokhov x = (signed char) data[2]; 48137767b66SDmitry Torokhov y = (signed char) data[3]; 4824104d13fSDmitry Torokhov 4834104d13fSDmitry Torokhov /* jitterable keeps track of whether any button has been pressed. 4844104d13fSDmitry Torokhov * We're also using it to remap the physical mouse button mask 4854104d13fSDmitry Torokhov * to pseudo-settings. (We don't specifically care about it's 4864104d13fSDmitry Torokhov * value after moving/transposing mouse button bitmasks, except 4874104d13fSDmitry Torokhov * that a non-zero value indicates that one or more 4884104d13fSDmitry Torokhov * mouse button was pressed.) 4894104d13fSDmitry Torokhov */ 4900038cae0SMark Vytlacil jitterable = data[1] & 0x07; 4914104d13fSDmitry Torokhov 4920038cae0SMark Vytlacil left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0; 4930038cae0SMark Vytlacil right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0; 4940038cae0SMark Vytlacil middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0; 4954104d13fSDmitry Torokhov 4964104d13fSDmitry Torokhov input_report_key(inputdev, BTN_LEFT, left); 4974104d13fSDmitry Torokhov input_report_key(inputdev, BTN_MIDDLE, middle); 4984104d13fSDmitry Torokhov input_report_key(inputdev, BTN_RIGHT, right); 4990038cae0SMark Vytlacil 5000038cae0SMark Vytlacil input_report_abs(inputdev, ABS_MISC, 5010038cae0SMark Vytlacil 1 | AIPTEK_REPORT_TOOL_UNKNOWN); 5024104d13fSDmitry Torokhov input_report_rel(inputdev, REL_X, x); 5034104d13fSDmitry Torokhov input_report_rel(inputdev, REL_Y, y); 5044104d13fSDmitry Torokhov 5054104d13fSDmitry Torokhov /* Wheel support is in the form of a single-event 5064104d13fSDmitry Torokhov * firing. 5074104d13fSDmitry Torokhov */ 5084104d13fSDmitry Torokhov if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { 5094104d13fSDmitry Torokhov input_report_rel(inputdev, REL_WHEEL, 5104104d13fSDmitry Torokhov aiptek->curSetting.wheel); 5114104d13fSDmitry Torokhov aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; 5124104d13fSDmitry Torokhov } 5131e7b3faeSRene van Paassen if (aiptek->lastMacro != -1) { 5141e7b3faeSRene van Paassen input_report_key(inputdev, 5151e7b3faeSRene van Paassen macroKeyEvents[aiptek->lastMacro], 0); 5161e7b3faeSRene van Paassen aiptek->lastMacro = -1; 5171e7b3faeSRene van Paassen } 5184104d13fSDmitry Torokhov input_sync(inputdev); 5194104d13fSDmitry Torokhov } 5204104d13fSDmitry Torokhov } 5214104d13fSDmitry Torokhov /* Report 2 is delivered only by the stylus, and delivers 5224104d13fSDmitry Torokhov * absolute coordinates. 5234104d13fSDmitry Torokhov */ 5244104d13fSDmitry Torokhov else if (data[0] == 2) { 5254104d13fSDmitry Torokhov if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { 5264104d13fSDmitry Torokhov aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; 5274104d13fSDmitry Torokhov } else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE 5284104d13fSDmitry Torokhov (aiptek->curSetting.pointerMode)) { 5294104d13fSDmitry Torokhov aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; 5304104d13fSDmitry Torokhov } else { 531858ad08cSHarvey Harrison x = get_unaligned_le16(data + 1); 532858ad08cSHarvey Harrison y = get_unaligned_le16(data + 3); 533858ad08cSHarvey Harrison z = get_unaligned_le16(data + 6); 5344104d13fSDmitry Torokhov 535fe981f23SRene van Paassen dv = (data[5] & 0x01) != 0 ? 1 : 0; 536fe981f23SRene van Paassen p = (data[5] & 0x02) != 0 ? 1 : 0; 5374104d13fSDmitry Torokhov tip = (data[5] & 0x04) != 0 ? 1 : 0; 5384104d13fSDmitry Torokhov 5394104d13fSDmitry Torokhov /* Use jitterable to re-arrange button masks 5404104d13fSDmitry Torokhov */ 5414104d13fSDmitry Torokhov jitterable = data[5] & 0x18; 5424104d13fSDmitry Torokhov 5434104d13fSDmitry Torokhov bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; 5444104d13fSDmitry Torokhov pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; 5454104d13fSDmitry Torokhov 5464104d13fSDmitry Torokhov /* dv indicates 'data valid' (e.g., the tablet is in sync 5474104d13fSDmitry Torokhov * and has delivered a "correct" report) We will ignore 5484104d13fSDmitry Torokhov * all 'bad' reports... 5494104d13fSDmitry Torokhov */ 5504104d13fSDmitry Torokhov if (dv != 0) { 551b3b6cf1dSRene van Paassen /* If the selected tool changed, reset the old 552b3b6cf1dSRene van Paassen * tool key, and set the new one. 5534104d13fSDmitry Torokhov */ 554b3b6cf1dSRene van Paassen if (aiptek->previousToolMode != 555b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode) { 5564104d13fSDmitry Torokhov input_report_key(inputdev, 557b3b6cf1dSRene van Paassen aiptek->previousToolMode, 0); 558b3b6cf1dSRene van Paassen input_report_key(inputdev, 559b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode, 5604104d13fSDmitry Torokhov 1); 561b3b6cf1dSRene van Paassen aiptek->previousToolMode = 562b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode; 5634104d13fSDmitry Torokhov } 5644104d13fSDmitry Torokhov 5654104d13fSDmitry Torokhov if (p != 0) { 5664104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_X, x); 5674104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_Y, y); 5684104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_PRESSURE, z); 5694104d13fSDmitry Torokhov 5704104d13fSDmitry Torokhov input_report_key(inputdev, BTN_TOUCH, tip); 5714104d13fSDmitry Torokhov input_report_key(inputdev, BTN_STYLUS, bs); 5724104d13fSDmitry Torokhov input_report_key(inputdev, BTN_STYLUS2, pck); 5734104d13fSDmitry Torokhov 5744104d13fSDmitry Torokhov if (aiptek->curSetting.xTilt != 5754104d13fSDmitry Torokhov AIPTEK_TILT_DISABLE) { 5764104d13fSDmitry Torokhov input_report_abs(inputdev, 5774104d13fSDmitry Torokhov ABS_TILT_X, 5784104d13fSDmitry Torokhov aiptek->curSetting.xTilt); 5794104d13fSDmitry Torokhov } 5804104d13fSDmitry Torokhov if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) { 5814104d13fSDmitry Torokhov input_report_abs(inputdev, 5824104d13fSDmitry Torokhov ABS_TILT_Y, 5834104d13fSDmitry Torokhov aiptek->curSetting.yTilt); 5844104d13fSDmitry Torokhov } 5854104d13fSDmitry Torokhov 5864104d13fSDmitry Torokhov /* Wheel support is in the form of a single-event 5874104d13fSDmitry Torokhov * firing. 5884104d13fSDmitry Torokhov */ 5894104d13fSDmitry Torokhov if (aiptek->curSetting.wheel != 5904104d13fSDmitry Torokhov AIPTEK_WHEEL_DISABLE) { 5914104d13fSDmitry Torokhov input_report_abs(inputdev, 5924104d13fSDmitry Torokhov ABS_WHEEL, 5934104d13fSDmitry Torokhov aiptek->curSetting.wheel); 5944104d13fSDmitry Torokhov aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; 5954104d13fSDmitry Torokhov } 5964104d13fSDmitry Torokhov } 5974104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS); 5981e7b3faeSRene van Paassen if (aiptek->lastMacro != -1) { 5991e7b3faeSRene van Paassen input_report_key(inputdev, 6001e7b3faeSRene van Paassen macroKeyEvents[aiptek->lastMacro], 0); 6011e7b3faeSRene van Paassen aiptek->lastMacro = -1; 6021e7b3faeSRene van Paassen } 6034104d13fSDmitry Torokhov input_sync(inputdev); 6044104d13fSDmitry Torokhov } 6054104d13fSDmitry Torokhov } 6064104d13fSDmitry Torokhov } 6074104d13fSDmitry Torokhov /* Report 3's come from the mouse in absolute mode. 6084104d13fSDmitry Torokhov */ 6094104d13fSDmitry Torokhov else if (data[0] == 3) { 6104104d13fSDmitry Torokhov if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { 6114104d13fSDmitry Torokhov aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; 6124104d13fSDmitry Torokhov } else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE 6134104d13fSDmitry Torokhov (aiptek->curSetting.pointerMode)) { 6144104d13fSDmitry Torokhov aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; 6154104d13fSDmitry Torokhov } else { 616858ad08cSHarvey Harrison x = get_unaligned_le16(data + 1); 617858ad08cSHarvey Harrison y = get_unaligned_le16(data + 3); 6184104d13fSDmitry Torokhov 6194104d13fSDmitry Torokhov jitterable = data[5] & 0x1c; 6204104d13fSDmitry Torokhov 621fe981f23SRene van Paassen dv = (data[5] & 0x01) != 0 ? 1 : 0; 622fe981f23SRene van Paassen p = (data[5] & 0x02) != 0 ? 1 : 0; 6234104d13fSDmitry Torokhov left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; 6244104d13fSDmitry Torokhov right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; 6254104d13fSDmitry Torokhov middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; 6264104d13fSDmitry Torokhov 6274104d13fSDmitry Torokhov if (dv != 0) { 628b3b6cf1dSRene van Paassen /* If the selected tool changed, reset the old 629b3b6cf1dSRene van Paassen * tool key, and set the new one. 6304104d13fSDmitry Torokhov */ 631b3b6cf1dSRene van Paassen if (aiptek->previousToolMode != 632b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode) { 6334104d13fSDmitry Torokhov input_report_key(inputdev, 634b3b6cf1dSRene van Paassen aiptek->previousToolMode, 0); 635b3b6cf1dSRene van Paassen input_report_key(inputdev, 636b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode, 6374104d13fSDmitry Torokhov 1); 638b3b6cf1dSRene van Paassen aiptek->previousToolMode = 639b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode; 6404104d13fSDmitry Torokhov } 6414104d13fSDmitry Torokhov 6424104d13fSDmitry Torokhov if (p != 0) { 6434104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_X, x); 6444104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_Y, y); 6454104d13fSDmitry Torokhov 6464104d13fSDmitry Torokhov input_report_key(inputdev, BTN_LEFT, left); 6474104d13fSDmitry Torokhov input_report_key(inputdev, BTN_MIDDLE, middle); 6484104d13fSDmitry Torokhov input_report_key(inputdev, BTN_RIGHT, right); 6494104d13fSDmitry Torokhov 6504104d13fSDmitry Torokhov /* Wheel support is in the form of a single-event 6514104d13fSDmitry Torokhov * firing. 6524104d13fSDmitry Torokhov */ 6534104d13fSDmitry Torokhov if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { 6544104d13fSDmitry Torokhov input_report_abs(inputdev, 6554104d13fSDmitry Torokhov ABS_WHEEL, 6564104d13fSDmitry Torokhov aiptek->curSetting.wheel); 6574104d13fSDmitry Torokhov aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; 6584104d13fSDmitry Torokhov } 6594104d13fSDmitry Torokhov } 660da9fda43SRene van Paassen input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); 6611e7b3faeSRene van Paassen if (aiptek->lastMacro != -1) { 6621e7b3faeSRene van Paassen input_report_key(inputdev, 6631e7b3faeSRene van Paassen macroKeyEvents[aiptek->lastMacro], 0); 6641e7b3faeSRene van Paassen aiptek->lastMacro = -1; 6651e7b3faeSRene van Paassen } 6664104d13fSDmitry Torokhov input_sync(inputdev); 6674104d13fSDmitry Torokhov } 6684104d13fSDmitry Torokhov } 6694104d13fSDmitry Torokhov } 6704104d13fSDmitry Torokhov /* Report 4s come from the macro keys when pressed by stylus 6714104d13fSDmitry Torokhov */ 6724104d13fSDmitry Torokhov else if (data[0] == 4) { 6734104d13fSDmitry Torokhov jitterable = data[1] & 0x18; 6744104d13fSDmitry Torokhov 675fe981f23SRene van Paassen dv = (data[1] & 0x01) != 0 ? 1 : 0; 676fe981f23SRene van Paassen p = (data[1] & 0x02) != 0 ? 1 : 0; 6774104d13fSDmitry Torokhov tip = (data[1] & 0x04) != 0 ? 1 : 0; 6784104d13fSDmitry Torokhov bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; 6794104d13fSDmitry Torokhov pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; 6804104d13fSDmitry Torokhov 6811e7b3faeSRene van Paassen macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; 682858ad08cSHarvey Harrison z = get_unaligned_le16(data + 4); 6834104d13fSDmitry Torokhov 6841e7b3faeSRene van Paassen if (dv) { 685b3b6cf1dSRene van Paassen /* If the selected tool changed, reset the old 686b3b6cf1dSRene van Paassen * tool key, and set the new one. 6874104d13fSDmitry Torokhov */ 688b3b6cf1dSRene van Paassen if (aiptek->previousToolMode != 689b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode) { 6904104d13fSDmitry Torokhov input_report_key(inputdev, 691b3b6cf1dSRene van Paassen aiptek->previousToolMode, 0); 692b3b6cf1dSRene van Paassen input_report_key(inputdev, 693b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode, 6944104d13fSDmitry Torokhov 1); 695b3b6cf1dSRene van Paassen aiptek->previousToolMode = 696b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode; 6974104d13fSDmitry Torokhov } 6984104d13fSDmitry Torokhov } 6994104d13fSDmitry Torokhov 7001e7b3faeSRene van Paassen if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { 7011e7b3faeSRene van Paassen input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); 7021e7b3faeSRene van Paassen aiptek->lastMacro = -1; 7034104d13fSDmitry Torokhov } 7041e7b3faeSRene van Paassen 7051e7b3faeSRene van Paassen if (macro != -1 && macro != aiptek->lastMacro) { 7061e7b3faeSRene van Paassen input_report_key(inputdev, macroKeyEvents[macro], 1); 7071e7b3faeSRene van Paassen aiptek->lastMacro = macro; 7084104d13fSDmitry Torokhov } 7094104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_MISC, 7104104d13fSDmitry Torokhov p | AIPTEK_REPORT_TOOL_STYLUS); 7114104d13fSDmitry Torokhov input_sync(inputdev); 7124104d13fSDmitry Torokhov } 7134104d13fSDmitry Torokhov /* Report 5s come from the macro keys when pressed by mouse 7144104d13fSDmitry Torokhov */ 7154104d13fSDmitry Torokhov else if (data[0] == 5) { 7164104d13fSDmitry Torokhov jitterable = data[1] & 0x1c; 7174104d13fSDmitry Torokhov 718fe981f23SRene van Paassen dv = (data[1] & 0x01) != 0 ? 1 : 0; 719fe981f23SRene van Paassen p = (data[1] & 0x02) != 0 ? 1 : 0; 7204104d13fSDmitry Torokhov left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; 7214104d13fSDmitry Torokhov right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; 7224104d13fSDmitry Torokhov middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; 7231e7b3faeSRene van Paassen macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0; 7244104d13fSDmitry Torokhov 7251e7b3faeSRene van Paassen if (dv) { 726b3b6cf1dSRene van Paassen /* If the selected tool changed, reset the old 727b3b6cf1dSRene van Paassen * tool key, and set the new one. 7284104d13fSDmitry Torokhov */ 729b3b6cf1dSRene van Paassen if (aiptek->previousToolMode != 730b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode) { 7314104d13fSDmitry Torokhov input_report_key(inputdev, 732b3b6cf1dSRene van Paassen aiptek->previousToolMode, 0); 733b3b6cf1dSRene van Paassen input_report_key(inputdev, 7341e7b3faeSRene van Paassen aiptek->curSetting.toolMode, 1); 7351e7b3faeSRene van Paassen aiptek->previousToolMode = aiptek->curSetting.toolMode; 7361e7b3faeSRene van Paassen } 7374104d13fSDmitry Torokhov } 7384104d13fSDmitry Torokhov 7391e7b3faeSRene van Paassen if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { 7401e7b3faeSRene van Paassen input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); 7411e7b3faeSRene van Paassen aiptek->lastMacro = -1; 7424104d13fSDmitry Torokhov } 7434104d13fSDmitry Torokhov 7441e7b3faeSRene van Paassen if (macro != -1 && macro != aiptek->lastMacro) { 7454104d13fSDmitry Torokhov input_report_key(inputdev, macroKeyEvents[macro], 1); 7461e7b3faeSRene van Paassen aiptek->lastMacro = macro; 7471e7b3faeSRene van Paassen } 7481e7b3faeSRene van Paassen 7491e7b3faeSRene van Paassen input_report_abs(inputdev, ABS_MISC, 7504104d13fSDmitry Torokhov p | AIPTEK_REPORT_TOOL_MOUSE); 7514104d13fSDmitry Torokhov input_sync(inputdev); 7524104d13fSDmitry Torokhov } 7534104d13fSDmitry Torokhov /* We have no idea which tool can generate a report 6. Theoretically, 7544104d13fSDmitry Torokhov * neither need to, having been given reports 4 & 5 for such use. 7554104d13fSDmitry Torokhov * However, report 6 is the 'official-looking' report for macroKeys; 7564104d13fSDmitry Torokhov * reports 4 & 5 supposively are used to support unnamed, unknown 7574104d13fSDmitry Torokhov * hat switches (which just so happen to be the macroKeys.) 7584104d13fSDmitry Torokhov */ 7594104d13fSDmitry Torokhov else if (data[0] == 6) { 760858ad08cSHarvey Harrison macro = get_unaligned_le16(data + 1); 7614104d13fSDmitry Torokhov if (macro > 0) { 7624104d13fSDmitry Torokhov input_report_key(inputdev, macroKeyEvents[macro - 1], 7634104d13fSDmitry Torokhov 0); 7644104d13fSDmitry Torokhov } 7654104d13fSDmitry Torokhov if (macro < 25) { 7664104d13fSDmitry Torokhov input_report_key(inputdev, macroKeyEvents[macro + 1], 7674104d13fSDmitry Torokhov 0); 7684104d13fSDmitry Torokhov } 7694104d13fSDmitry Torokhov 770b3b6cf1dSRene van Paassen /* If the selected tool changed, reset the old 771b3b6cf1dSRene van Paassen tool key, and set the new one. 7724104d13fSDmitry Torokhov */ 773b3b6cf1dSRene van Paassen if (aiptek->previousToolMode != 774b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode) { 7754104d13fSDmitry Torokhov input_report_key(inputdev, 776b3b6cf1dSRene van Paassen aiptek->previousToolMode, 0); 777b3b6cf1dSRene van Paassen input_report_key(inputdev, 778b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode, 779b3b6cf1dSRene van Paassen 1); 780b3b6cf1dSRene van Paassen aiptek->previousToolMode = 781b3b6cf1dSRene van Paassen aiptek->curSetting.toolMode; 7824104d13fSDmitry Torokhov } 7834104d13fSDmitry Torokhov 7844104d13fSDmitry Torokhov input_report_key(inputdev, macroKeyEvents[macro], 1); 7854104d13fSDmitry Torokhov input_report_abs(inputdev, ABS_MISC, 7864104d13fSDmitry Torokhov 1 | AIPTEK_REPORT_TOOL_UNKNOWN); 7874104d13fSDmitry Torokhov input_sync(inputdev); 7884104d13fSDmitry Torokhov } else { 7894104d13fSDmitry Torokhov dbg("Unknown report %d", data[0]); 7904104d13fSDmitry Torokhov } 7914104d13fSDmitry Torokhov 7924104d13fSDmitry Torokhov /* Jitter may occur when the user presses a button on the stlyus 7934104d13fSDmitry Torokhov * or the mouse. What we do to prevent that is wait 'x' milliseconds 7944104d13fSDmitry Torokhov * following a 'jitterable' event, which should give the hand some time 7954104d13fSDmitry Torokhov * stabilize itself. 7964104d13fSDmitry Torokhov * 7974104d13fSDmitry Torokhov * We just introduced aiptek->previousJitterable to carry forth the 7984104d13fSDmitry Torokhov * notion that jitter occurs when the button state changes from on to off: 7994104d13fSDmitry Torokhov * a person drawing, holding a button down is not subject to jittering. 8004104d13fSDmitry Torokhov * With that in mind, changing from upper button depressed to lower button 8014104d13fSDmitry Torokhov * WILL transition through a jitter delay. 8024104d13fSDmitry Torokhov */ 8034104d13fSDmitry Torokhov 8044104d13fSDmitry Torokhov if (aiptek->previousJitterable != jitterable && 8054104d13fSDmitry Torokhov aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) { 8064104d13fSDmitry Torokhov aiptek->endDelay = jiffies + 8074104d13fSDmitry Torokhov ((aiptek->curSetting.jitterDelay * HZ) / 1000); 8084104d13fSDmitry Torokhov aiptek->inDelay = 1; 8094104d13fSDmitry Torokhov } 8104104d13fSDmitry Torokhov aiptek->previousJitterable = jitterable; 8114104d13fSDmitry Torokhov 8124104d13fSDmitry Torokhov exit: 8134104d13fSDmitry Torokhov retval = usb_submit_urb(urb, GFP_ATOMIC); 8144104d13fSDmitry Torokhov if (retval != 0) { 8154104d13fSDmitry Torokhov err("%s - usb_submit_urb failed with result %d", 816ea3e6c59SHarvey Harrison __func__, retval); 8174104d13fSDmitry Torokhov } 8184104d13fSDmitry Torokhov } 8194104d13fSDmitry Torokhov 8204104d13fSDmitry Torokhov /*********************************************************************** 8214104d13fSDmitry Torokhov * These are the USB id's known so far. We do not identify them to 8224104d13fSDmitry Torokhov * specific Aiptek model numbers, because there has been overlaps, 8234104d13fSDmitry Torokhov * use, and reuse of id's in existing models. Certain models have 8244104d13fSDmitry Torokhov * been known to use more than one ID, indicative perhaps of 8254104d13fSDmitry Torokhov * manufacturing revisions. In any event, we consider these 8264104d13fSDmitry Torokhov * IDs to not be model-specific nor unique. 8274104d13fSDmitry Torokhov */ 8284104d13fSDmitry Torokhov static const struct usb_device_id aiptek_ids[] = { 8294104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)}, 8304104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)}, 8314104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)}, 8324104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)}, 8334104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)}, 8344104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)}, 8354104d13fSDmitry Torokhov {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)}, 836a32bcc45SGuryanov Dmitry {USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)}, 8374104d13fSDmitry Torokhov {} 8384104d13fSDmitry Torokhov }; 8394104d13fSDmitry Torokhov 8404104d13fSDmitry Torokhov MODULE_DEVICE_TABLE(usb, aiptek_ids); 8414104d13fSDmitry Torokhov 8424104d13fSDmitry Torokhov /*********************************************************************** 8434104d13fSDmitry Torokhov * Open an instance of the tablet driver. 8444104d13fSDmitry Torokhov */ 8454104d13fSDmitry Torokhov static int aiptek_open(struct input_dev *inputdev) 8464104d13fSDmitry Torokhov { 8474104d13fSDmitry Torokhov struct aiptek *aiptek = input_get_drvdata(inputdev); 8484104d13fSDmitry Torokhov 8494104d13fSDmitry Torokhov aiptek->urb->dev = aiptek->usbdev; 8504104d13fSDmitry Torokhov if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) 8514104d13fSDmitry Torokhov return -EIO; 8524104d13fSDmitry Torokhov 8534104d13fSDmitry Torokhov return 0; 8544104d13fSDmitry Torokhov } 8554104d13fSDmitry Torokhov 8564104d13fSDmitry Torokhov /*********************************************************************** 8574104d13fSDmitry Torokhov * Close an instance of the tablet driver. 8584104d13fSDmitry Torokhov */ 8594104d13fSDmitry Torokhov static void aiptek_close(struct input_dev *inputdev) 8604104d13fSDmitry Torokhov { 8614104d13fSDmitry Torokhov struct aiptek *aiptek = input_get_drvdata(inputdev); 8624104d13fSDmitry Torokhov 8634104d13fSDmitry Torokhov usb_kill_urb(aiptek->urb); 8644104d13fSDmitry Torokhov } 8654104d13fSDmitry Torokhov 8664104d13fSDmitry Torokhov /*********************************************************************** 8674104d13fSDmitry Torokhov * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, 8684104d13fSDmitry Torokhov * where they were known as usb_set_report and usb_get_report. 8694104d13fSDmitry Torokhov */ 8704104d13fSDmitry Torokhov static int 8714104d13fSDmitry Torokhov aiptek_set_report(struct aiptek *aiptek, 8724104d13fSDmitry Torokhov unsigned char report_type, 8734104d13fSDmitry Torokhov unsigned char report_id, void *buffer, int size) 8744104d13fSDmitry Torokhov { 8754104d13fSDmitry Torokhov return usb_control_msg(aiptek->usbdev, 8764104d13fSDmitry Torokhov usb_sndctrlpipe(aiptek->usbdev, 0), 8774104d13fSDmitry Torokhov USB_REQ_SET_REPORT, 8784104d13fSDmitry Torokhov USB_TYPE_CLASS | USB_RECIP_INTERFACE | 8794104d13fSDmitry Torokhov USB_DIR_OUT, (report_type << 8) + report_id, 8804104d13fSDmitry Torokhov aiptek->ifnum, buffer, size, 5000); 8814104d13fSDmitry Torokhov } 8824104d13fSDmitry Torokhov 8834104d13fSDmitry Torokhov static int 8844104d13fSDmitry Torokhov aiptek_get_report(struct aiptek *aiptek, 8854104d13fSDmitry Torokhov unsigned char report_type, 8864104d13fSDmitry Torokhov unsigned char report_id, void *buffer, int size) 8874104d13fSDmitry Torokhov { 8884104d13fSDmitry Torokhov return usb_control_msg(aiptek->usbdev, 8894104d13fSDmitry Torokhov usb_rcvctrlpipe(aiptek->usbdev, 0), 8904104d13fSDmitry Torokhov USB_REQ_GET_REPORT, 8914104d13fSDmitry Torokhov USB_TYPE_CLASS | USB_RECIP_INTERFACE | 8924104d13fSDmitry Torokhov USB_DIR_IN, (report_type << 8) + report_id, 8934104d13fSDmitry Torokhov aiptek->ifnum, buffer, size, 5000); 8944104d13fSDmitry Torokhov } 8954104d13fSDmitry Torokhov 8964104d13fSDmitry Torokhov /*********************************************************************** 8974104d13fSDmitry Torokhov * Send a command to the tablet. 8984104d13fSDmitry Torokhov */ 8994104d13fSDmitry Torokhov static int 9004104d13fSDmitry Torokhov aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data) 9014104d13fSDmitry Torokhov { 9024104d13fSDmitry Torokhov const int sizeof_buf = 3 * sizeof(u8); 9034104d13fSDmitry Torokhov int ret; 9044104d13fSDmitry Torokhov u8 *buf; 9054104d13fSDmitry Torokhov 9064104d13fSDmitry Torokhov buf = kmalloc(sizeof_buf, GFP_KERNEL); 9074104d13fSDmitry Torokhov if (!buf) 9084104d13fSDmitry Torokhov return -ENOMEM; 9094104d13fSDmitry Torokhov 9104104d13fSDmitry Torokhov buf[0] = 2; 9114104d13fSDmitry Torokhov buf[1] = command; 9124104d13fSDmitry Torokhov buf[2] = data; 9134104d13fSDmitry Torokhov 9144104d13fSDmitry Torokhov if ((ret = 9154104d13fSDmitry Torokhov aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { 9164104d13fSDmitry Torokhov dbg("aiptek_program: failed, tried to send: 0x%02x 0x%02x", 9174104d13fSDmitry Torokhov command, data); 9184104d13fSDmitry Torokhov } 9194104d13fSDmitry Torokhov kfree(buf); 9204104d13fSDmitry Torokhov return ret < 0 ? ret : 0; 9214104d13fSDmitry Torokhov } 9224104d13fSDmitry Torokhov 9234104d13fSDmitry Torokhov /*********************************************************************** 9244104d13fSDmitry Torokhov * Retrieve information from the tablet. Querying info is defined as first 9254104d13fSDmitry Torokhov * sending the {command,data} sequence as a command, followed by a wait 9264104d13fSDmitry Torokhov * (aka, "programmaticDelay") and then a "read" request. 9274104d13fSDmitry Torokhov */ 9284104d13fSDmitry Torokhov static int 9294104d13fSDmitry Torokhov aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data) 9304104d13fSDmitry Torokhov { 9314104d13fSDmitry Torokhov const int sizeof_buf = 3 * sizeof(u8); 9324104d13fSDmitry Torokhov int ret; 9334104d13fSDmitry Torokhov u8 *buf; 9344104d13fSDmitry Torokhov 9354104d13fSDmitry Torokhov buf = kmalloc(sizeof_buf, GFP_KERNEL); 9364104d13fSDmitry Torokhov if (!buf) 9374104d13fSDmitry Torokhov return -ENOMEM; 9384104d13fSDmitry Torokhov 9394104d13fSDmitry Torokhov buf[0] = 2; 9404104d13fSDmitry Torokhov buf[1] = command; 9414104d13fSDmitry Torokhov buf[2] = data; 9424104d13fSDmitry Torokhov 9434104d13fSDmitry Torokhov if (aiptek_command(aiptek, command, data) != 0) { 9444104d13fSDmitry Torokhov kfree(buf); 9454104d13fSDmitry Torokhov return -EIO; 9464104d13fSDmitry Torokhov } 9474104d13fSDmitry Torokhov msleep(aiptek->curSetting.programmableDelay); 9484104d13fSDmitry Torokhov 9494104d13fSDmitry Torokhov if ((ret = 9504104d13fSDmitry Torokhov aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { 9514104d13fSDmitry Torokhov dbg("aiptek_query failed: returned 0x%02x 0x%02x 0x%02x", 9524104d13fSDmitry Torokhov buf[0], buf[1], buf[2]); 9534104d13fSDmitry Torokhov ret = -EIO; 9544104d13fSDmitry Torokhov } else { 955858ad08cSHarvey Harrison ret = get_unaligned_le16(buf + 1); 9564104d13fSDmitry Torokhov } 9574104d13fSDmitry Torokhov kfree(buf); 9584104d13fSDmitry Torokhov return ret; 9594104d13fSDmitry Torokhov } 9604104d13fSDmitry Torokhov 9614104d13fSDmitry Torokhov /*********************************************************************** 9624104d13fSDmitry Torokhov * Program the tablet into either absolute or relative mode. 9634104d13fSDmitry Torokhov * We also get information about the tablet's size. 9644104d13fSDmitry Torokhov */ 9654104d13fSDmitry Torokhov static int aiptek_program_tablet(struct aiptek *aiptek) 9664104d13fSDmitry Torokhov { 9674104d13fSDmitry Torokhov int ret; 9684104d13fSDmitry Torokhov /* Execute Resolution500LPI */ 9694104d13fSDmitry Torokhov if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0) 9704104d13fSDmitry Torokhov return ret; 9714104d13fSDmitry Torokhov 9724104d13fSDmitry Torokhov /* Query getModelCode */ 9734104d13fSDmitry Torokhov if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0) 9744104d13fSDmitry Torokhov return ret; 9754104d13fSDmitry Torokhov aiptek->features.modelCode = ret & 0xff; 9764104d13fSDmitry Torokhov 9774104d13fSDmitry Torokhov /* Query getODMCode */ 9784104d13fSDmitry Torokhov if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0) 9794104d13fSDmitry Torokhov return ret; 9804104d13fSDmitry Torokhov aiptek->features.odmCode = ret; 9814104d13fSDmitry Torokhov 9824104d13fSDmitry Torokhov /* Query getFirmwareCode */ 9834104d13fSDmitry Torokhov if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0) 9844104d13fSDmitry Torokhov return ret; 9854104d13fSDmitry Torokhov aiptek->features.firmwareCode = ret; 9864104d13fSDmitry Torokhov 9874104d13fSDmitry Torokhov /* Query getXextension */ 9884104d13fSDmitry Torokhov if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0) 9894104d13fSDmitry Torokhov return ret; 9904104d13fSDmitry Torokhov aiptek->inputdev->absmin[ABS_X] = 0; 9914104d13fSDmitry Torokhov aiptek->inputdev->absmax[ABS_X] = ret - 1; 9924104d13fSDmitry Torokhov 9934104d13fSDmitry Torokhov /* Query getYextension */ 9944104d13fSDmitry Torokhov if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0) 9954104d13fSDmitry Torokhov return ret; 9964104d13fSDmitry Torokhov aiptek->inputdev->absmin[ABS_Y] = 0; 9974104d13fSDmitry Torokhov aiptek->inputdev->absmax[ABS_Y] = ret - 1; 9984104d13fSDmitry Torokhov 9994104d13fSDmitry Torokhov /* Query getPressureLevels */ 10004104d13fSDmitry Torokhov if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0) 10014104d13fSDmitry Torokhov return ret; 10024104d13fSDmitry Torokhov aiptek->inputdev->absmin[ABS_PRESSURE] = 0; 10034104d13fSDmitry Torokhov aiptek->inputdev->absmax[ABS_PRESSURE] = ret - 1; 10044104d13fSDmitry Torokhov 10054104d13fSDmitry Torokhov /* Depending on whether we are in absolute or relative mode, we will 10064104d13fSDmitry Torokhov * do a switchToTablet(absolute) or switchToMouse(relative) command. 10074104d13fSDmitry Torokhov */ 10084104d13fSDmitry Torokhov if (aiptek->curSetting.coordinateMode == 10094104d13fSDmitry Torokhov AIPTEK_COORDINATE_ABSOLUTE_MODE) { 10104104d13fSDmitry Torokhov /* Execute switchToTablet */ 10114104d13fSDmitry Torokhov if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) { 10124104d13fSDmitry Torokhov return ret; 10134104d13fSDmitry Torokhov } 10144104d13fSDmitry Torokhov } else { 10154104d13fSDmitry Torokhov /* Execute switchToMouse */ 10164104d13fSDmitry Torokhov if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) { 10174104d13fSDmitry Torokhov return ret; 10184104d13fSDmitry Torokhov } 10194104d13fSDmitry Torokhov } 10204104d13fSDmitry Torokhov 10214104d13fSDmitry Torokhov /* Enable the macro keys */ 10224104d13fSDmitry Torokhov if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0) 10234104d13fSDmitry Torokhov return ret; 10244104d13fSDmitry Torokhov #if 0 10254104d13fSDmitry Torokhov /* Execute FilterOn */ 10264104d13fSDmitry Torokhov if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0) 10274104d13fSDmitry Torokhov return ret; 10284104d13fSDmitry Torokhov #endif 10294104d13fSDmitry Torokhov 10304104d13fSDmitry Torokhov /* Execute AutoGainOn */ 10314104d13fSDmitry Torokhov if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0) 10324104d13fSDmitry Torokhov return ret; 10334104d13fSDmitry Torokhov 10344104d13fSDmitry Torokhov /* Reset the eventCount, so we track events from last (re)programming 10354104d13fSDmitry Torokhov */ 10364104d13fSDmitry Torokhov aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA; 10374104d13fSDmitry Torokhov aiptek->eventCount = 0; 10384104d13fSDmitry Torokhov 10394104d13fSDmitry Torokhov return 0; 10404104d13fSDmitry Torokhov } 10414104d13fSDmitry Torokhov 10424104d13fSDmitry Torokhov /*********************************************************************** 10434104d13fSDmitry Torokhov * Sysfs functions. Sysfs prefers that individually-tunable parameters 10444104d13fSDmitry Torokhov * exist in their separate pseudo-files. Summary data that is immutable 10454104d13fSDmitry Torokhov * may exist in a singular file so long as you don't define a writeable 10464104d13fSDmitry Torokhov * interface. 10474104d13fSDmitry Torokhov */ 10484104d13fSDmitry Torokhov 10494104d13fSDmitry Torokhov /*********************************************************************** 10504104d13fSDmitry Torokhov * support the 'size' file -- display support 10514104d13fSDmitry Torokhov */ 10524104d13fSDmitry Torokhov static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf) 10534104d13fSDmitry Torokhov { 10544104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 10554104d13fSDmitry Torokhov 10564104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%dx%d\n", 10574104d13fSDmitry Torokhov aiptek->inputdev->absmax[ABS_X] + 1, 10584104d13fSDmitry Torokhov aiptek->inputdev->absmax[ABS_Y] + 1); 10594104d13fSDmitry Torokhov } 10604104d13fSDmitry Torokhov 10614104d13fSDmitry Torokhov /* These structs define the sysfs files, param #1 is the name of the 10624104d13fSDmitry Torokhov * file, param 2 is the file permissions, param 3 & 4 are to the 10634104d13fSDmitry Torokhov * output generator and input parser routines. Absence of a routine is 10644104d13fSDmitry Torokhov * permitted -- it only means can't either 'cat' the file, or send data 10654104d13fSDmitry Torokhov * to it. 10664104d13fSDmitry Torokhov */ 10674104d13fSDmitry Torokhov static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL); 10684104d13fSDmitry Torokhov 10694104d13fSDmitry Torokhov /*********************************************************************** 10704104d13fSDmitry Torokhov * support routines for the 'pointer_mode' file. Note that this file 10714104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 10724104d13fSDmitry Torokhov */ 1073cd438a58SDmitry Torokhov static struct aiptek_map pointer_mode_map[] = { 1074cd438a58SDmitry Torokhov { "stylus", AIPTEK_POINTER_ONLY_STYLUS_MODE }, 1075cd438a58SDmitry Torokhov { "mouse", AIPTEK_POINTER_ONLY_MOUSE_MODE }, 1076cd438a58SDmitry Torokhov { "either", AIPTEK_POINTER_EITHER_MODE }, 1077cd438a58SDmitry Torokhov { NULL, AIPTEK_INVALID_VALUE } 1078cd438a58SDmitry Torokhov }; 1079cd438a58SDmitry Torokhov 10804104d13fSDmitry Torokhov static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf) 10814104d13fSDmitry Torokhov { 10824104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 10834104d13fSDmitry Torokhov 1084cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1085cd438a58SDmitry Torokhov map_val_to_str(pointer_mode_map, 1086cd438a58SDmitry Torokhov aiptek->curSetting.pointerMode)); 10874104d13fSDmitry Torokhov } 10884104d13fSDmitry Torokhov 10894104d13fSDmitry Torokhov static ssize_t 10904104d13fSDmitry Torokhov store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 10914104d13fSDmitry Torokhov { 10924104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1093cd438a58SDmitry Torokhov int new_mode = map_str_to_val(pointer_mode_map, buf, count); 10944104d13fSDmitry Torokhov 1095cd438a58SDmitry Torokhov if (new_mode == AIPTEK_INVALID_VALUE) 1096cd438a58SDmitry Torokhov return -EINVAL; 1097cd438a58SDmitry Torokhov 1098cd438a58SDmitry Torokhov aiptek->newSetting.pointerMode = new_mode; 10994104d13fSDmitry Torokhov return count; 11004104d13fSDmitry Torokhov } 11014104d13fSDmitry Torokhov 11024104d13fSDmitry Torokhov static DEVICE_ATTR(pointer_mode, 11034104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 11044104d13fSDmitry Torokhov show_tabletPointerMode, store_tabletPointerMode); 11054104d13fSDmitry Torokhov 11064104d13fSDmitry Torokhov /*********************************************************************** 11074104d13fSDmitry Torokhov * support routines for the 'coordinate_mode' file. Note that this file 11084104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 11094104d13fSDmitry Torokhov */ 1110cd438a58SDmitry Torokhov 1111cd438a58SDmitry Torokhov static struct aiptek_map coordinate_mode_map[] = { 1112cd438a58SDmitry Torokhov { "absolute", AIPTEK_COORDINATE_ABSOLUTE_MODE }, 1113cd438a58SDmitry Torokhov { "relative", AIPTEK_COORDINATE_RELATIVE_MODE }, 1114cd438a58SDmitry Torokhov { NULL, AIPTEK_INVALID_VALUE } 1115cd438a58SDmitry Torokhov }; 1116cd438a58SDmitry Torokhov 11174104d13fSDmitry Torokhov static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf) 11184104d13fSDmitry Torokhov { 11194104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 11204104d13fSDmitry Torokhov 1121cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1122cd438a58SDmitry Torokhov map_val_to_str(coordinate_mode_map, 1123cd438a58SDmitry Torokhov aiptek->curSetting.coordinateMode)); 11244104d13fSDmitry Torokhov } 11254104d13fSDmitry Torokhov 11264104d13fSDmitry Torokhov static ssize_t 11274104d13fSDmitry Torokhov store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 11284104d13fSDmitry Torokhov { 11294104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1130cd438a58SDmitry Torokhov int new_mode = map_str_to_val(coordinate_mode_map, buf, count); 11314104d13fSDmitry Torokhov 1132cd438a58SDmitry Torokhov if (new_mode == AIPTEK_INVALID_VALUE) 1133cd438a58SDmitry Torokhov return -EINVAL; 1134cd438a58SDmitry Torokhov 1135cd438a58SDmitry Torokhov aiptek->newSetting.coordinateMode = new_mode; 11364104d13fSDmitry Torokhov return count; 11374104d13fSDmitry Torokhov } 11384104d13fSDmitry Torokhov 11394104d13fSDmitry Torokhov static DEVICE_ATTR(coordinate_mode, 11404104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 11414104d13fSDmitry Torokhov show_tabletCoordinateMode, store_tabletCoordinateMode); 11424104d13fSDmitry Torokhov 11434104d13fSDmitry Torokhov /*********************************************************************** 11444104d13fSDmitry Torokhov * support routines for the 'tool_mode' file. Note that this file 11454104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 11464104d13fSDmitry Torokhov */ 1147cd438a58SDmitry Torokhov 1148cd438a58SDmitry Torokhov static struct aiptek_map tool_mode_map[] = { 1149cd438a58SDmitry Torokhov { "mouse", AIPTEK_TOOL_BUTTON_MOUSE_MODE }, 1150cd438a58SDmitry Torokhov { "eraser", AIPTEK_TOOL_BUTTON_ERASER_MODE }, 1151cd438a58SDmitry Torokhov { "pencil", AIPTEK_TOOL_BUTTON_PENCIL_MODE }, 1152cd438a58SDmitry Torokhov { "pen", AIPTEK_TOOL_BUTTON_PEN_MODE }, 1153cd438a58SDmitry Torokhov { "brush", AIPTEK_TOOL_BUTTON_BRUSH_MODE }, 1154cd438a58SDmitry Torokhov { "airbrush", AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE }, 1155cd438a58SDmitry Torokhov { "lens", AIPTEK_TOOL_BUTTON_LENS_MODE }, 1156cd438a58SDmitry Torokhov { NULL, AIPTEK_INVALID_VALUE } 1157cd438a58SDmitry Torokhov }; 1158cd438a58SDmitry Torokhov 11594104d13fSDmitry Torokhov static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf) 11604104d13fSDmitry Torokhov { 11614104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 11624104d13fSDmitry Torokhov 1163cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1164cd438a58SDmitry Torokhov map_val_to_str(tool_mode_map, 1165cd438a58SDmitry Torokhov aiptek->curSetting.toolMode)); 11664104d13fSDmitry Torokhov } 11674104d13fSDmitry Torokhov 11684104d13fSDmitry Torokhov static ssize_t 11694104d13fSDmitry Torokhov store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 11704104d13fSDmitry Torokhov { 11714104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1172cd438a58SDmitry Torokhov int new_mode = map_str_to_val(tool_mode_map, buf, count); 11734104d13fSDmitry Torokhov 1174cd438a58SDmitry Torokhov if (new_mode == AIPTEK_INVALID_VALUE) 1175cd438a58SDmitry Torokhov return -EINVAL; 11764104d13fSDmitry Torokhov 1177cd438a58SDmitry Torokhov aiptek->newSetting.toolMode = new_mode; 11784104d13fSDmitry Torokhov return count; 11794104d13fSDmitry Torokhov } 11804104d13fSDmitry Torokhov 11814104d13fSDmitry Torokhov static DEVICE_ATTR(tool_mode, 11824104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 11834104d13fSDmitry Torokhov show_tabletToolMode, store_tabletToolMode); 11844104d13fSDmitry Torokhov 11854104d13fSDmitry Torokhov /*********************************************************************** 11864104d13fSDmitry Torokhov * support routines for the 'xtilt' file. Note that this file 11874104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 11884104d13fSDmitry Torokhov */ 11894104d13fSDmitry Torokhov static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf) 11904104d13fSDmitry Torokhov { 11914104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 11924104d13fSDmitry Torokhov 11934104d13fSDmitry Torokhov if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) { 11944104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "disable\n"); 11954104d13fSDmitry Torokhov } else { 11964104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%d\n", 11974104d13fSDmitry Torokhov aiptek->curSetting.xTilt); 11984104d13fSDmitry Torokhov } 11994104d13fSDmitry Torokhov } 12004104d13fSDmitry Torokhov 12014104d13fSDmitry Torokhov static ssize_t 12024104d13fSDmitry Torokhov store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 12034104d13fSDmitry Torokhov { 12044104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1205160f1fefSJoe Rouvier long x; 12064104d13fSDmitry Torokhov 1207160f1fefSJoe Rouvier if (strict_strtol(buf, 10, &x)) { 1208160f1fefSJoe Rouvier size_t len = buf[count - 1] == '\n' ? count - 1 : count; 1209160f1fefSJoe Rouvier 1210160f1fefSJoe Rouvier if (strncmp(buf, "disable", len)) 1211160f1fefSJoe Rouvier return -EINVAL; 1212160f1fefSJoe Rouvier 12134104d13fSDmitry Torokhov aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE; 12144104d13fSDmitry Torokhov } else { 1215160f1fefSJoe Rouvier if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX) 1216160f1fefSJoe Rouvier return -EINVAL; 1217160f1fefSJoe Rouvier 12184104d13fSDmitry Torokhov aiptek->newSetting.xTilt = x; 12194104d13fSDmitry Torokhov } 1220160f1fefSJoe Rouvier 12214104d13fSDmitry Torokhov return count; 12224104d13fSDmitry Torokhov } 12234104d13fSDmitry Torokhov 12244104d13fSDmitry Torokhov static DEVICE_ATTR(xtilt, 12254104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, show_tabletXtilt, store_tabletXtilt); 12264104d13fSDmitry Torokhov 12274104d13fSDmitry Torokhov /*********************************************************************** 12284104d13fSDmitry Torokhov * support routines for the 'ytilt' file. Note that this file 12294104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 12304104d13fSDmitry Torokhov */ 12314104d13fSDmitry Torokhov static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf) 12324104d13fSDmitry Torokhov { 12334104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 12344104d13fSDmitry Torokhov 12354104d13fSDmitry Torokhov if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) { 12364104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "disable\n"); 12374104d13fSDmitry Torokhov } else { 12384104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%d\n", 12394104d13fSDmitry Torokhov aiptek->curSetting.yTilt); 12404104d13fSDmitry Torokhov } 12414104d13fSDmitry Torokhov } 12424104d13fSDmitry Torokhov 12434104d13fSDmitry Torokhov static ssize_t 12444104d13fSDmitry Torokhov store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 12454104d13fSDmitry Torokhov { 12464104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1247160f1fefSJoe Rouvier long y; 12484104d13fSDmitry Torokhov 1249160f1fefSJoe Rouvier if (strict_strtol(buf, 10, &y)) { 1250160f1fefSJoe Rouvier size_t len = buf[count - 1] == '\n' ? count - 1 : count; 1251160f1fefSJoe Rouvier 1252160f1fefSJoe Rouvier if (strncmp(buf, "disable", len)) 1253160f1fefSJoe Rouvier return -EINVAL; 1254160f1fefSJoe Rouvier 12554104d13fSDmitry Torokhov aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE; 12564104d13fSDmitry Torokhov } else { 1257160f1fefSJoe Rouvier if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX) 1258160f1fefSJoe Rouvier return -EINVAL; 1259160f1fefSJoe Rouvier 12604104d13fSDmitry Torokhov aiptek->newSetting.yTilt = y; 12614104d13fSDmitry Torokhov } 1262160f1fefSJoe Rouvier 12634104d13fSDmitry Torokhov return count; 12644104d13fSDmitry Torokhov } 12654104d13fSDmitry Torokhov 12664104d13fSDmitry Torokhov static DEVICE_ATTR(ytilt, 12674104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, show_tabletYtilt, store_tabletYtilt); 12684104d13fSDmitry Torokhov 12694104d13fSDmitry Torokhov /*********************************************************************** 12704104d13fSDmitry Torokhov * support routines for the 'jitter' file. Note that this file 12714104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 12724104d13fSDmitry Torokhov */ 12734104d13fSDmitry Torokhov static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf) 12744104d13fSDmitry Torokhov { 12754104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 12764104d13fSDmitry Torokhov 12774104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); 12784104d13fSDmitry Torokhov } 12794104d13fSDmitry Torokhov 12804104d13fSDmitry Torokhov static ssize_t 12814104d13fSDmitry Torokhov store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 12824104d13fSDmitry Torokhov { 12834104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1284160f1fefSJoe Rouvier long j; 12854104d13fSDmitry Torokhov 1286160f1fefSJoe Rouvier if (strict_strtol(buf, 10, &j)) 1287160f1fefSJoe Rouvier return -EINVAL; 1288160f1fefSJoe Rouvier 1289160f1fefSJoe Rouvier aiptek->newSetting.jitterDelay = (int)j; 12904104d13fSDmitry Torokhov return count; 12914104d13fSDmitry Torokhov } 12924104d13fSDmitry Torokhov 12934104d13fSDmitry Torokhov static DEVICE_ATTR(jitter, 12944104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 12954104d13fSDmitry Torokhov show_tabletJitterDelay, store_tabletJitterDelay); 12964104d13fSDmitry Torokhov 12974104d13fSDmitry Torokhov /*********************************************************************** 12984104d13fSDmitry Torokhov * support routines for the 'delay' file. Note that this file 12994104d13fSDmitry Torokhov * both displays current setting and allows reprogramming. 13004104d13fSDmitry Torokhov */ 13014104d13fSDmitry Torokhov static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf) 13024104d13fSDmitry Torokhov { 13034104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 13044104d13fSDmitry Torokhov 13054104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%d\n", 13064104d13fSDmitry Torokhov aiptek->curSetting.programmableDelay); 13074104d13fSDmitry Torokhov } 13084104d13fSDmitry Torokhov 13094104d13fSDmitry Torokhov static ssize_t 13104104d13fSDmitry Torokhov store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 13114104d13fSDmitry Torokhov { 13124104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1313160f1fefSJoe Rouvier long d; 13144104d13fSDmitry Torokhov 1315160f1fefSJoe Rouvier if (strict_strtol(buf, 10, &d)) 1316160f1fefSJoe Rouvier return -EINVAL; 1317160f1fefSJoe Rouvier 1318160f1fefSJoe Rouvier aiptek->newSetting.programmableDelay = (int)d; 13194104d13fSDmitry Torokhov return count; 13204104d13fSDmitry Torokhov } 13214104d13fSDmitry Torokhov 13224104d13fSDmitry Torokhov static DEVICE_ATTR(delay, 13234104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 13244104d13fSDmitry Torokhov show_tabletProgrammableDelay, store_tabletProgrammableDelay); 13254104d13fSDmitry Torokhov 13264104d13fSDmitry Torokhov /*********************************************************************** 13274104d13fSDmitry Torokhov * support routines for the 'event_count' file. Note that this file 13284104d13fSDmitry Torokhov * only displays current setting. 13294104d13fSDmitry Torokhov */ 13304104d13fSDmitry Torokhov static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf) 13314104d13fSDmitry Torokhov { 13324104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 13334104d13fSDmitry Torokhov 13344104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); 13354104d13fSDmitry Torokhov } 13364104d13fSDmitry Torokhov 13374104d13fSDmitry Torokhov static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL); 13384104d13fSDmitry Torokhov 13394104d13fSDmitry Torokhov /*********************************************************************** 13404104d13fSDmitry Torokhov * support routines for the 'diagnostic' file. Note that this file 13414104d13fSDmitry Torokhov * only displays current setting. 13424104d13fSDmitry Torokhov */ 13434104d13fSDmitry Torokhov static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf) 13444104d13fSDmitry Torokhov { 13454104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 13464104d13fSDmitry Torokhov char *retMsg; 13474104d13fSDmitry Torokhov 13484104d13fSDmitry Torokhov switch (aiptek->diagnostic) { 13494104d13fSDmitry Torokhov case AIPTEK_DIAGNOSTIC_NA: 13504104d13fSDmitry Torokhov retMsg = "no errors\n"; 13514104d13fSDmitry Torokhov break; 13524104d13fSDmitry Torokhov 13534104d13fSDmitry Torokhov case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE: 13544104d13fSDmitry Torokhov retMsg = "Error: receiving relative reports\n"; 13554104d13fSDmitry Torokhov break; 13564104d13fSDmitry Torokhov 13574104d13fSDmitry Torokhov case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE: 13584104d13fSDmitry Torokhov retMsg = "Error: receiving absolute reports\n"; 13594104d13fSDmitry Torokhov break; 13604104d13fSDmitry Torokhov 13614104d13fSDmitry Torokhov case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED: 13624104d13fSDmitry Torokhov if (aiptek->curSetting.pointerMode == 13634104d13fSDmitry Torokhov AIPTEK_POINTER_ONLY_MOUSE_MODE) { 13644104d13fSDmitry Torokhov retMsg = "Error: receiving stylus reports\n"; 13654104d13fSDmitry Torokhov } else { 13664104d13fSDmitry Torokhov retMsg = "Error: receiving mouse reports\n"; 13674104d13fSDmitry Torokhov } 13684104d13fSDmitry Torokhov break; 13694104d13fSDmitry Torokhov 13704104d13fSDmitry Torokhov default: 13714104d13fSDmitry Torokhov return 0; 13724104d13fSDmitry Torokhov } 13734104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, retMsg); 13744104d13fSDmitry Torokhov } 13754104d13fSDmitry Torokhov 13764104d13fSDmitry Torokhov static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL); 13774104d13fSDmitry Torokhov 13784104d13fSDmitry Torokhov /*********************************************************************** 13794104d13fSDmitry Torokhov * support routines for the 'stylus_upper' file. Note that this file 13804104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 13814104d13fSDmitry Torokhov */ 1382cd438a58SDmitry Torokhov 1383cd438a58SDmitry Torokhov static struct aiptek_map stylus_button_map[] = { 1384cd438a58SDmitry Torokhov { "upper", AIPTEK_STYLUS_UPPER_BUTTON }, 1385cd438a58SDmitry Torokhov { "lower", AIPTEK_STYLUS_LOWER_BUTTON }, 1386cd438a58SDmitry Torokhov { NULL, AIPTEK_INVALID_VALUE } 1387cd438a58SDmitry Torokhov }; 1388cd438a58SDmitry Torokhov 13894104d13fSDmitry Torokhov static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf) 13904104d13fSDmitry Torokhov { 13914104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 13924104d13fSDmitry Torokhov 1393cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1394cd438a58SDmitry Torokhov map_val_to_str(stylus_button_map, 1395cd438a58SDmitry Torokhov aiptek->curSetting.stylusButtonUpper)); 13964104d13fSDmitry Torokhov } 13974104d13fSDmitry Torokhov 13984104d13fSDmitry Torokhov static ssize_t 13994104d13fSDmitry Torokhov store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14004104d13fSDmitry Torokhov { 14014104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1402cd438a58SDmitry Torokhov int new_button = map_str_to_val(stylus_button_map, buf, count); 14034104d13fSDmitry Torokhov 1404cd438a58SDmitry Torokhov if (new_button == AIPTEK_INVALID_VALUE) 1405cd438a58SDmitry Torokhov return -EINVAL; 1406cd438a58SDmitry Torokhov 1407cd438a58SDmitry Torokhov aiptek->newSetting.stylusButtonUpper = new_button; 14084104d13fSDmitry Torokhov return count; 14094104d13fSDmitry Torokhov } 14104104d13fSDmitry Torokhov 14114104d13fSDmitry Torokhov static DEVICE_ATTR(stylus_upper, 14124104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 14134104d13fSDmitry Torokhov show_tabletStylusUpper, store_tabletStylusUpper); 14144104d13fSDmitry Torokhov 14154104d13fSDmitry Torokhov /*********************************************************************** 14164104d13fSDmitry Torokhov * support routines for the 'stylus_lower' file. Note that this file 14174104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 14184104d13fSDmitry Torokhov */ 1419cd438a58SDmitry Torokhov 14204104d13fSDmitry Torokhov static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf) 14214104d13fSDmitry Torokhov { 14224104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 14234104d13fSDmitry Torokhov 1424cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1425cd438a58SDmitry Torokhov map_val_to_str(stylus_button_map, 1426cd438a58SDmitry Torokhov aiptek->curSetting.stylusButtonLower)); 14274104d13fSDmitry Torokhov } 14284104d13fSDmitry Torokhov 14294104d13fSDmitry Torokhov static ssize_t 14304104d13fSDmitry Torokhov store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14314104d13fSDmitry Torokhov { 14324104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1433cd438a58SDmitry Torokhov int new_button = map_str_to_val(stylus_button_map, buf, count); 14344104d13fSDmitry Torokhov 1435cd438a58SDmitry Torokhov if (new_button == AIPTEK_INVALID_VALUE) 1436cd438a58SDmitry Torokhov return -EINVAL; 1437cd438a58SDmitry Torokhov 1438cd438a58SDmitry Torokhov aiptek->newSetting.stylusButtonLower = new_button; 14394104d13fSDmitry Torokhov return count; 14404104d13fSDmitry Torokhov } 14414104d13fSDmitry Torokhov 14424104d13fSDmitry Torokhov static DEVICE_ATTR(stylus_lower, 14434104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 14444104d13fSDmitry Torokhov show_tabletStylusLower, store_tabletStylusLower); 14454104d13fSDmitry Torokhov 14464104d13fSDmitry Torokhov /*********************************************************************** 14474104d13fSDmitry Torokhov * support routines for the 'mouse_left' file. Note that this file 14484104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 14494104d13fSDmitry Torokhov */ 1450cd438a58SDmitry Torokhov 1451cd438a58SDmitry Torokhov static struct aiptek_map mouse_button_map[] = { 1452cd438a58SDmitry Torokhov { "left", AIPTEK_MOUSE_LEFT_BUTTON }, 1453cd438a58SDmitry Torokhov { "middle", AIPTEK_MOUSE_MIDDLE_BUTTON }, 1454cd438a58SDmitry Torokhov { "right", AIPTEK_MOUSE_RIGHT_BUTTON }, 1455cd438a58SDmitry Torokhov { NULL, AIPTEK_INVALID_VALUE } 1456cd438a58SDmitry Torokhov }; 1457cd438a58SDmitry Torokhov 14584104d13fSDmitry Torokhov static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf) 14594104d13fSDmitry Torokhov { 14604104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 14614104d13fSDmitry Torokhov 1462cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1463cd438a58SDmitry Torokhov map_val_to_str(mouse_button_map, 1464cd438a58SDmitry Torokhov aiptek->curSetting.mouseButtonLeft)); 14654104d13fSDmitry Torokhov } 14664104d13fSDmitry Torokhov 14674104d13fSDmitry Torokhov static ssize_t 14684104d13fSDmitry Torokhov store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14694104d13fSDmitry Torokhov { 14704104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1471cd438a58SDmitry Torokhov int new_button = map_str_to_val(mouse_button_map, buf, count); 14724104d13fSDmitry Torokhov 1473cd438a58SDmitry Torokhov if (new_button == AIPTEK_INVALID_VALUE) 1474cd438a58SDmitry Torokhov return -EINVAL; 1475cd438a58SDmitry Torokhov 1476cd438a58SDmitry Torokhov aiptek->newSetting.mouseButtonLeft = new_button; 14774104d13fSDmitry Torokhov return count; 14784104d13fSDmitry Torokhov } 14794104d13fSDmitry Torokhov 14804104d13fSDmitry Torokhov static DEVICE_ATTR(mouse_left, 14814104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 14824104d13fSDmitry Torokhov show_tabletMouseLeft, store_tabletMouseLeft); 14834104d13fSDmitry Torokhov 14844104d13fSDmitry Torokhov /*********************************************************************** 14854104d13fSDmitry Torokhov * support routines for the 'mouse_middle' file. Note that this file 14864104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 14874104d13fSDmitry Torokhov */ 14884104d13fSDmitry Torokhov static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf) 14894104d13fSDmitry Torokhov { 14904104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 14914104d13fSDmitry Torokhov 1492cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1493cd438a58SDmitry Torokhov map_val_to_str(mouse_button_map, 1494cd438a58SDmitry Torokhov aiptek->curSetting.mouseButtonMiddle)); 14954104d13fSDmitry Torokhov } 14964104d13fSDmitry Torokhov 14974104d13fSDmitry Torokhov static ssize_t 14984104d13fSDmitry Torokhov store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14994104d13fSDmitry Torokhov { 15004104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1501cd438a58SDmitry Torokhov int new_button = map_str_to_val(mouse_button_map, buf, count); 15024104d13fSDmitry Torokhov 1503cd438a58SDmitry Torokhov if (new_button == AIPTEK_INVALID_VALUE) 1504cd438a58SDmitry Torokhov return -EINVAL; 1505cd438a58SDmitry Torokhov 1506cd438a58SDmitry Torokhov aiptek->newSetting.mouseButtonMiddle = new_button; 15074104d13fSDmitry Torokhov return count; 15084104d13fSDmitry Torokhov } 15094104d13fSDmitry Torokhov 15104104d13fSDmitry Torokhov static DEVICE_ATTR(mouse_middle, 15114104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 15124104d13fSDmitry Torokhov show_tabletMouseMiddle, store_tabletMouseMiddle); 15134104d13fSDmitry Torokhov 15144104d13fSDmitry Torokhov /*********************************************************************** 15154104d13fSDmitry Torokhov * support routines for the 'mouse_right' file. Note that this file 15164104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 15174104d13fSDmitry Torokhov */ 15184104d13fSDmitry Torokhov static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf) 15194104d13fSDmitry Torokhov { 15204104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 15214104d13fSDmitry Torokhov 1522cd438a58SDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%s\n", 1523cd438a58SDmitry Torokhov map_val_to_str(mouse_button_map, 1524cd438a58SDmitry Torokhov aiptek->curSetting.mouseButtonRight)); 15254104d13fSDmitry Torokhov } 15264104d13fSDmitry Torokhov 15274104d13fSDmitry Torokhov static ssize_t 15284104d13fSDmitry Torokhov store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15294104d13fSDmitry Torokhov { 15304104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1531cd438a58SDmitry Torokhov int new_button = map_str_to_val(mouse_button_map, buf, count); 15324104d13fSDmitry Torokhov 1533cd438a58SDmitry Torokhov if (new_button == AIPTEK_INVALID_VALUE) 1534cd438a58SDmitry Torokhov return -EINVAL; 1535cd438a58SDmitry Torokhov 1536cd438a58SDmitry Torokhov aiptek->newSetting.mouseButtonRight = new_button; 15374104d13fSDmitry Torokhov return count; 15384104d13fSDmitry Torokhov } 15394104d13fSDmitry Torokhov 15404104d13fSDmitry Torokhov static DEVICE_ATTR(mouse_right, 15414104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, 15424104d13fSDmitry Torokhov show_tabletMouseRight, store_tabletMouseRight); 15434104d13fSDmitry Torokhov 15444104d13fSDmitry Torokhov /*********************************************************************** 15454104d13fSDmitry Torokhov * support routines for the 'wheel' file. Note that this file 15464104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 15474104d13fSDmitry Torokhov */ 15484104d13fSDmitry Torokhov static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf) 15494104d13fSDmitry Torokhov { 15504104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 15514104d13fSDmitry Torokhov 15524104d13fSDmitry Torokhov if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) { 15534104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "disable\n"); 15544104d13fSDmitry Torokhov } else { 15554104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%d\n", 15564104d13fSDmitry Torokhov aiptek->curSetting.wheel); 15574104d13fSDmitry Torokhov } 15584104d13fSDmitry Torokhov } 15594104d13fSDmitry Torokhov 15604104d13fSDmitry Torokhov static ssize_t 15614104d13fSDmitry Torokhov store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15624104d13fSDmitry Torokhov { 15634104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 1564160f1fefSJoe Rouvier long w; 15654104d13fSDmitry Torokhov 1566160f1fefSJoe Rouvier if (strict_strtol(buf, 10, &w)) return -EINVAL; 1567160f1fefSJoe Rouvier 1568160f1fefSJoe Rouvier aiptek->newSetting.wheel = (int)w; 15694104d13fSDmitry Torokhov return count; 15704104d13fSDmitry Torokhov } 15714104d13fSDmitry Torokhov 15724104d13fSDmitry Torokhov static DEVICE_ATTR(wheel, 15734104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, show_tabletWheel, store_tabletWheel); 15744104d13fSDmitry Torokhov 15754104d13fSDmitry Torokhov /*********************************************************************** 15764104d13fSDmitry Torokhov * support routines for the 'execute' file. Note that this file 15774104d13fSDmitry Torokhov * both displays current setting and allows for setting changing. 15784104d13fSDmitry Torokhov */ 15794104d13fSDmitry Torokhov static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf) 15804104d13fSDmitry Torokhov { 15814104d13fSDmitry Torokhov /* There is nothing useful to display, so a one-line manual 15824104d13fSDmitry Torokhov * is in order... 15834104d13fSDmitry Torokhov */ 15844104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, 15854104d13fSDmitry Torokhov "Write anything to this file to program your tablet.\n"); 15864104d13fSDmitry Torokhov } 15874104d13fSDmitry Torokhov 15884104d13fSDmitry Torokhov static ssize_t 15894104d13fSDmitry Torokhov store_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15904104d13fSDmitry Torokhov { 15914104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 15924104d13fSDmitry Torokhov 15934104d13fSDmitry Torokhov /* We do not care what you write to this file. Merely the action 15944104d13fSDmitry Torokhov * of writing to this file triggers a tablet reprogramming. 15954104d13fSDmitry Torokhov */ 15964104d13fSDmitry Torokhov memcpy(&aiptek->curSetting, &aiptek->newSetting, 15974104d13fSDmitry Torokhov sizeof(struct aiptek_settings)); 15984104d13fSDmitry Torokhov 15994104d13fSDmitry Torokhov if (aiptek_program_tablet(aiptek) < 0) 16004104d13fSDmitry Torokhov return -EIO; 16014104d13fSDmitry Torokhov 16024104d13fSDmitry Torokhov return count; 16034104d13fSDmitry Torokhov } 16044104d13fSDmitry Torokhov 16054104d13fSDmitry Torokhov static DEVICE_ATTR(execute, 16064104d13fSDmitry Torokhov S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute); 16074104d13fSDmitry Torokhov 16084104d13fSDmitry Torokhov /*********************************************************************** 16094104d13fSDmitry Torokhov * support routines for the 'odm_code' file. Note that this file 16104104d13fSDmitry Torokhov * only displays current setting. 16114104d13fSDmitry Torokhov */ 16124104d13fSDmitry Torokhov static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf) 16134104d13fSDmitry Torokhov { 16144104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 16154104d13fSDmitry Torokhov 16164104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); 16174104d13fSDmitry Torokhov } 16184104d13fSDmitry Torokhov 16194104d13fSDmitry Torokhov static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL); 16204104d13fSDmitry Torokhov 16214104d13fSDmitry Torokhov /*********************************************************************** 16224104d13fSDmitry Torokhov * support routines for the 'model_code' file. Note that this file 16234104d13fSDmitry Torokhov * only displays current setting. 16244104d13fSDmitry Torokhov */ 16254104d13fSDmitry Torokhov static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf) 16264104d13fSDmitry Torokhov { 16274104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 16284104d13fSDmitry Torokhov 16294104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); 16304104d13fSDmitry Torokhov } 16314104d13fSDmitry Torokhov 16324104d13fSDmitry Torokhov static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL); 16334104d13fSDmitry Torokhov 16344104d13fSDmitry Torokhov /*********************************************************************** 16354104d13fSDmitry Torokhov * support routines for the 'firmware_code' file. Note that this file 16364104d13fSDmitry Torokhov * only displays current setting. 16374104d13fSDmitry Torokhov */ 16384104d13fSDmitry Torokhov static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf) 16394104d13fSDmitry Torokhov { 16404104d13fSDmitry Torokhov struct aiptek *aiptek = dev_get_drvdata(dev); 16414104d13fSDmitry Torokhov 16424104d13fSDmitry Torokhov return snprintf(buf, PAGE_SIZE, "%04x\n", 16434104d13fSDmitry Torokhov aiptek->features.firmwareCode); 16444104d13fSDmitry Torokhov } 16454104d13fSDmitry Torokhov 16464104d13fSDmitry Torokhov static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL); 16474104d13fSDmitry Torokhov 1648b087e1f3SDmitry Torokhov static struct attribute *aiptek_attributes[] = { 1649b087e1f3SDmitry Torokhov &dev_attr_size.attr, 1650b087e1f3SDmitry Torokhov &dev_attr_pointer_mode.attr, 1651b087e1f3SDmitry Torokhov &dev_attr_coordinate_mode.attr, 1652b087e1f3SDmitry Torokhov &dev_attr_tool_mode.attr, 1653b087e1f3SDmitry Torokhov &dev_attr_xtilt.attr, 1654b087e1f3SDmitry Torokhov &dev_attr_ytilt.attr, 1655b087e1f3SDmitry Torokhov &dev_attr_jitter.attr, 1656b087e1f3SDmitry Torokhov &dev_attr_delay.attr, 1657b087e1f3SDmitry Torokhov &dev_attr_event_count.attr, 1658b087e1f3SDmitry Torokhov &dev_attr_diagnostic.attr, 1659b087e1f3SDmitry Torokhov &dev_attr_odm_code.attr, 1660b087e1f3SDmitry Torokhov &dev_attr_model_code.attr, 1661b087e1f3SDmitry Torokhov &dev_attr_firmware_code.attr, 1662b087e1f3SDmitry Torokhov &dev_attr_stylus_lower.attr, 1663b087e1f3SDmitry Torokhov &dev_attr_stylus_upper.attr, 1664b087e1f3SDmitry Torokhov &dev_attr_mouse_left.attr, 1665b087e1f3SDmitry Torokhov &dev_attr_mouse_middle.attr, 1666b087e1f3SDmitry Torokhov &dev_attr_mouse_right.attr, 1667b087e1f3SDmitry Torokhov &dev_attr_wheel.attr, 1668b087e1f3SDmitry Torokhov &dev_attr_execute.attr, 1669b087e1f3SDmitry Torokhov NULL 1670b087e1f3SDmitry Torokhov }; 16714104d13fSDmitry Torokhov 1672b087e1f3SDmitry Torokhov static struct attribute_group aiptek_attribute_group = { 1673b087e1f3SDmitry Torokhov .attrs = aiptek_attributes, 1674b087e1f3SDmitry Torokhov }; 16754104d13fSDmitry Torokhov 16764104d13fSDmitry Torokhov /*********************************************************************** 16774104d13fSDmitry Torokhov * This routine is called when a tablet has been identified. It basically 16784104d13fSDmitry Torokhov * sets up the tablet and the driver's internal structures. 16794104d13fSDmitry Torokhov */ 16804104d13fSDmitry Torokhov static int 16814104d13fSDmitry Torokhov aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) 16824104d13fSDmitry Torokhov { 16834104d13fSDmitry Torokhov struct usb_device *usbdev = interface_to_usbdev(intf); 16844104d13fSDmitry Torokhov struct usb_endpoint_descriptor *endpoint; 16854104d13fSDmitry Torokhov struct aiptek *aiptek; 16864104d13fSDmitry Torokhov struct input_dev *inputdev; 16874104d13fSDmitry Torokhov int i; 16884104d13fSDmitry Torokhov int speeds[] = { 0, 16894104d13fSDmitry Torokhov AIPTEK_PROGRAMMABLE_DELAY_50, 16904104d13fSDmitry Torokhov AIPTEK_PROGRAMMABLE_DELAY_400, 16914104d13fSDmitry Torokhov AIPTEK_PROGRAMMABLE_DELAY_25, 16924104d13fSDmitry Torokhov AIPTEK_PROGRAMMABLE_DELAY_100, 16934104d13fSDmitry Torokhov AIPTEK_PROGRAMMABLE_DELAY_200, 16944104d13fSDmitry Torokhov AIPTEK_PROGRAMMABLE_DELAY_300 16954104d13fSDmitry Torokhov }; 16964104d13fSDmitry Torokhov int err = -ENOMEM; 16974104d13fSDmitry Torokhov 16984104d13fSDmitry Torokhov /* programmableDelay is where the command-line specified 16994104d13fSDmitry Torokhov * delay is kept. We make it the first element of speeds[], 17004104d13fSDmitry Torokhov * so therefore, your override speed is tried first, then the 17014104d13fSDmitry Torokhov * remainder. Note that the default value of 400ms will be tried 17024104d13fSDmitry Torokhov * if you do not specify any command line parameter. 17034104d13fSDmitry Torokhov */ 17044104d13fSDmitry Torokhov speeds[0] = programmableDelay; 17054104d13fSDmitry Torokhov 17064104d13fSDmitry Torokhov aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL); 17074104d13fSDmitry Torokhov inputdev = input_allocate_device(); 17086125a400SRene van Paassen if (!aiptek || !inputdev) { 17091817b169SGreg Kroah-Hartman dev_warn(&intf->dev, 17101817b169SGreg Kroah-Hartman "cannot allocate memory or input device\n"); 17114104d13fSDmitry Torokhov goto fail1; 17126125a400SRene van Paassen } 17134104d13fSDmitry Torokhov 1714*997ea58eSDaniel Mack aiptek->data = usb_alloc_coherent(usbdev, AIPTEK_PACKET_LENGTH, 17154104d13fSDmitry Torokhov GFP_ATOMIC, &aiptek->data_dma); 17166125a400SRene van Paassen if (!aiptek->data) { 17171817b169SGreg Kroah-Hartman dev_warn(&intf->dev, "cannot allocate usb buffer\n"); 17184104d13fSDmitry Torokhov goto fail1; 17196125a400SRene van Paassen } 17204104d13fSDmitry Torokhov 17214104d13fSDmitry Torokhov aiptek->urb = usb_alloc_urb(0, GFP_KERNEL); 17226125a400SRene van Paassen if (!aiptek->urb) { 17231817b169SGreg Kroah-Hartman dev_warn(&intf->dev, "cannot allocate urb\n"); 17244104d13fSDmitry Torokhov goto fail2; 17256125a400SRene van Paassen } 17264104d13fSDmitry Torokhov 17274104d13fSDmitry Torokhov aiptek->inputdev = inputdev; 17284104d13fSDmitry Torokhov aiptek->usbdev = usbdev; 17294104d13fSDmitry Torokhov aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber; 17304104d13fSDmitry Torokhov aiptek->inDelay = 0; 17314104d13fSDmitry Torokhov aiptek->endDelay = 0; 17324104d13fSDmitry Torokhov aiptek->previousJitterable = 0; 17331e7b3faeSRene van Paassen aiptek->lastMacro = -1; 17344104d13fSDmitry Torokhov 17354104d13fSDmitry Torokhov /* Set up the curSettings struct. Said struct contains the current 17364104d13fSDmitry Torokhov * programmable parameters. The newSetting struct contains changes 17374104d13fSDmitry Torokhov * the user makes to the settings via the sysfs interface. Those 17384104d13fSDmitry Torokhov * changes are not "committed" to curSettings until the user 17394104d13fSDmitry Torokhov * writes to the sysfs/.../execute file. 17404104d13fSDmitry Torokhov */ 17414104d13fSDmitry Torokhov aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE; 17424104d13fSDmitry Torokhov aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE; 17434104d13fSDmitry Torokhov aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE; 17444104d13fSDmitry Torokhov aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE; 17454104d13fSDmitry Torokhov aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE; 17464104d13fSDmitry Torokhov aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON; 17474104d13fSDmitry Torokhov aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON; 17484104d13fSDmitry Torokhov aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON; 17494104d13fSDmitry Torokhov aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON; 17504104d13fSDmitry Torokhov aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON; 17514104d13fSDmitry Torokhov aiptek->curSetting.jitterDelay = jitterDelay; 17524104d13fSDmitry Torokhov aiptek->curSetting.programmableDelay = programmableDelay; 17534104d13fSDmitry Torokhov 17544104d13fSDmitry Torokhov /* Both structs should have equivalent settings 17554104d13fSDmitry Torokhov */ 17564104d13fSDmitry Torokhov aiptek->newSetting = aiptek->curSetting; 17574104d13fSDmitry Torokhov 17584104d13fSDmitry Torokhov /* Determine the usb devices' physical path. 17594104d13fSDmitry Torokhov * Asketh not why we always pretend we're using "../input0", 17604104d13fSDmitry Torokhov * but I suspect this will have to be refactored one 17614104d13fSDmitry Torokhov * day if a single USB device can be a keyboard & a mouse 17624104d13fSDmitry Torokhov * & a tablet, and the inputX number actually will tell 17634104d13fSDmitry Torokhov * us something... 17644104d13fSDmitry Torokhov */ 17654104d13fSDmitry Torokhov usb_make_path(usbdev, aiptek->features.usbPath, 17664104d13fSDmitry Torokhov sizeof(aiptek->features.usbPath)); 17674104d13fSDmitry Torokhov strlcat(aiptek->features.usbPath, "/input0", 17684104d13fSDmitry Torokhov sizeof(aiptek->features.usbPath)); 17694104d13fSDmitry Torokhov 17704104d13fSDmitry Torokhov /* Set up client data, pointers to open and close routines 17714104d13fSDmitry Torokhov * for the input device. 17724104d13fSDmitry Torokhov */ 17734104d13fSDmitry Torokhov inputdev->name = "Aiptek"; 17744104d13fSDmitry Torokhov inputdev->phys = aiptek->features.usbPath; 17754104d13fSDmitry Torokhov usb_to_input_id(usbdev, &inputdev->id); 17764104d13fSDmitry Torokhov inputdev->dev.parent = &intf->dev; 17774104d13fSDmitry Torokhov 17784104d13fSDmitry Torokhov input_set_drvdata(inputdev, aiptek); 17794104d13fSDmitry Torokhov 17804104d13fSDmitry Torokhov inputdev->open = aiptek_open; 17814104d13fSDmitry Torokhov inputdev->close = aiptek_close; 17824104d13fSDmitry Torokhov 17834104d13fSDmitry Torokhov /* Now program the capacities of the tablet, in terms of being 17844104d13fSDmitry Torokhov * an input device. 17854104d13fSDmitry Torokhov */ 17861a54f49eSRene van Paassen for (i = 0; i < ARRAY_SIZE(eventTypes); ++i) 17871a54f49eSRene van Paassen __set_bit(eventTypes[i], inputdev->evbit); 17884104d13fSDmitry Torokhov 17891a54f49eSRene van Paassen for (i = 0; i < ARRAY_SIZE(absEvents); ++i) 17901a54f49eSRene van Paassen __set_bit(absEvents[i], inputdev->absbit); 17914104d13fSDmitry Torokhov 17921a54f49eSRene van Paassen for (i = 0; i < ARRAY_SIZE(relEvents); ++i) 17931a54f49eSRene van Paassen __set_bit(relEvents[i], inputdev->relbit); 17944104d13fSDmitry Torokhov 17951a54f49eSRene van Paassen __set_bit(MSC_SERIAL, inputdev->mscbit); 17964104d13fSDmitry Torokhov 179733936fa6SDmitry Torokhov /* Set up key and button codes */ 179833936fa6SDmitry Torokhov for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i) 179933936fa6SDmitry Torokhov __set_bit(buttonEvents[i], inputdev->keybit); 180033936fa6SDmitry Torokhov 18014104d13fSDmitry Torokhov for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i) 180233936fa6SDmitry Torokhov __set_bit(macroKeyEvents[i], inputdev->keybit); 18034104d13fSDmitry Torokhov 18044104d13fSDmitry Torokhov /* 18054104d13fSDmitry Torokhov * Program the input device coordinate capacities. We do not yet 18064104d13fSDmitry Torokhov * know what maximum X, Y, and Z values are, so we're putting fake 18074104d13fSDmitry Torokhov * values in. Later, we'll ask the tablet to put in the correct 18084104d13fSDmitry Torokhov * values. 18094104d13fSDmitry Torokhov */ 18104104d13fSDmitry Torokhov input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0); 18114104d13fSDmitry Torokhov input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0); 18124104d13fSDmitry Torokhov input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0); 18134104d13fSDmitry Torokhov input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); 18144104d13fSDmitry Torokhov input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); 18154104d13fSDmitry Torokhov input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0); 18164104d13fSDmitry Torokhov 18174104d13fSDmitry Torokhov endpoint = &intf->altsetting[0].endpoint[0].desc; 18184104d13fSDmitry Torokhov 18194104d13fSDmitry Torokhov /* Go set up our URB, which is called when the tablet receives 18204104d13fSDmitry Torokhov * input. 18214104d13fSDmitry Torokhov */ 18224104d13fSDmitry Torokhov usb_fill_int_urb(aiptek->urb, 18234104d13fSDmitry Torokhov aiptek->usbdev, 18244104d13fSDmitry Torokhov usb_rcvintpipe(aiptek->usbdev, 18254104d13fSDmitry Torokhov endpoint->bEndpointAddress), 18264104d13fSDmitry Torokhov aiptek->data, 8, aiptek_irq, aiptek, 18274104d13fSDmitry Torokhov endpoint->bInterval); 18284104d13fSDmitry Torokhov 18294104d13fSDmitry Torokhov aiptek->urb->transfer_dma = aiptek->data_dma; 18304104d13fSDmitry Torokhov aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 18314104d13fSDmitry Torokhov 18324104d13fSDmitry Torokhov /* Program the tablet. This sets the tablet up in the mode 18334104d13fSDmitry Torokhov * specified in newSetting, and also queries the tablet's 18344104d13fSDmitry Torokhov * physical capacities. 18354104d13fSDmitry Torokhov * 18364104d13fSDmitry Torokhov * Sanity check: if a tablet doesn't like the slow programmatic 18374104d13fSDmitry Torokhov * delay, we often get sizes of 0x0. Let's use that as an indicator 18384104d13fSDmitry Torokhov * to try faster delays, up to 25 ms. If that logic fails, well, you'll 18394104d13fSDmitry Torokhov * have to explain to us how your tablet thinks it's 0x0, and yet that's 18404104d13fSDmitry Torokhov * not an error :-) 18414104d13fSDmitry Torokhov */ 18424104d13fSDmitry Torokhov 18434104d13fSDmitry Torokhov for (i = 0; i < ARRAY_SIZE(speeds); ++i) { 18444104d13fSDmitry Torokhov aiptek->curSetting.programmableDelay = speeds[i]; 18454104d13fSDmitry Torokhov (void)aiptek_program_tablet(aiptek); 18464104d13fSDmitry Torokhov if (aiptek->inputdev->absmax[ABS_X] > 0) { 1847899ef6e7SGreg Kroah-Hartman dev_info(&intf->dev, 1848899ef6e7SGreg Kroah-Hartman "Aiptek using %d ms programming speed\n", 18494104d13fSDmitry Torokhov aiptek->curSetting.programmableDelay); 18504104d13fSDmitry Torokhov break; 18514104d13fSDmitry Torokhov } 18524104d13fSDmitry Torokhov } 18534104d13fSDmitry Torokhov 18546125a400SRene van Paassen /* Murphy says that some day someone will have a tablet that fails the 18556125a400SRene van Paassen above test. That's you, Frederic Rodrigo */ 18566125a400SRene van Paassen if (i == ARRAY_SIZE(speeds)) { 1857899ef6e7SGreg Kroah-Hartman dev_info(&intf->dev, 1858899ef6e7SGreg Kroah-Hartman "Aiptek tried all speeds, no sane response\n"); 18596125a400SRene van Paassen goto fail2; 18606125a400SRene van Paassen } 18616125a400SRene van Paassen 18624104d13fSDmitry Torokhov /* Associate this driver's struct with the usb interface. 18634104d13fSDmitry Torokhov */ 18644104d13fSDmitry Torokhov usb_set_intfdata(intf, aiptek); 18654104d13fSDmitry Torokhov 18664104d13fSDmitry Torokhov /* Set up the sysfs files 18674104d13fSDmitry Torokhov */ 1868b087e1f3SDmitry Torokhov err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group); 18696125a400SRene van Paassen if (err) { 18701817b169SGreg Kroah-Hartman dev_warn(&intf->dev, "cannot create sysfs group err: %d\n", 18711817b169SGreg Kroah-Hartman err); 1872b087e1f3SDmitry Torokhov goto fail3; 18736125a400SRene van Paassen } 1874b087e1f3SDmitry Torokhov 1875b087e1f3SDmitry Torokhov /* Register the tablet as an Input Device 1876b087e1f3SDmitry Torokhov */ 1877b087e1f3SDmitry Torokhov err = input_register_device(aiptek->inputdev); 18786125a400SRene van Paassen if (err) { 18791817b169SGreg Kroah-Hartman dev_warn(&intf->dev, 18801817b169SGreg Kroah-Hartman "input_register_device returned err: %d\n", err); 1881b087e1f3SDmitry Torokhov goto fail4; 18826125a400SRene van Paassen } 18834104d13fSDmitry Torokhov return 0; 18844104d13fSDmitry Torokhov 1885b087e1f3SDmitry Torokhov fail4: sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); 1886b087e1f3SDmitry Torokhov fail3: usb_free_urb(aiptek->urb); 1887*997ea58eSDaniel Mack fail2: usb_free_coherent(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data, 18884104d13fSDmitry Torokhov aiptek->data_dma); 1889b087e1f3SDmitry Torokhov fail1: usb_set_intfdata(intf, NULL); 1890b087e1f3SDmitry Torokhov input_free_device(inputdev); 18914104d13fSDmitry Torokhov kfree(aiptek); 18924104d13fSDmitry Torokhov return err; 18934104d13fSDmitry Torokhov } 18944104d13fSDmitry Torokhov 18954104d13fSDmitry Torokhov /*********************************************************************** 18964104d13fSDmitry Torokhov * Deal with tablet disconnecting from the system. 18974104d13fSDmitry Torokhov */ 18984104d13fSDmitry Torokhov static void aiptek_disconnect(struct usb_interface *intf) 18994104d13fSDmitry Torokhov { 19004104d13fSDmitry Torokhov struct aiptek *aiptek = usb_get_intfdata(intf); 19014104d13fSDmitry Torokhov 19024104d13fSDmitry Torokhov /* Disassociate driver's struct with usb interface 19034104d13fSDmitry Torokhov */ 19044104d13fSDmitry Torokhov usb_set_intfdata(intf, NULL); 19054104d13fSDmitry Torokhov if (aiptek != NULL) { 19064104d13fSDmitry Torokhov /* Free & unhook everything from the system. 19074104d13fSDmitry Torokhov */ 19084104d13fSDmitry Torokhov usb_kill_urb(aiptek->urb); 19094104d13fSDmitry Torokhov input_unregister_device(aiptek->inputdev); 1910b087e1f3SDmitry Torokhov sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); 19114104d13fSDmitry Torokhov usb_free_urb(aiptek->urb); 1912*997ea58eSDaniel Mack usb_free_coherent(interface_to_usbdev(intf), 19134104d13fSDmitry Torokhov AIPTEK_PACKET_LENGTH, 19144104d13fSDmitry Torokhov aiptek->data, aiptek->data_dma); 19154104d13fSDmitry Torokhov kfree(aiptek); 19164104d13fSDmitry Torokhov } 19174104d13fSDmitry Torokhov } 19184104d13fSDmitry Torokhov 19194104d13fSDmitry Torokhov static struct usb_driver aiptek_driver = { 19204104d13fSDmitry Torokhov .name = "aiptek", 19214104d13fSDmitry Torokhov .probe = aiptek_probe, 19224104d13fSDmitry Torokhov .disconnect = aiptek_disconnect, 19234104d13fSDmitry Torokhov .id_table = aiptek_ids, 19244104d13fSDmitry Torokhov }; 19254104d13fSDmitry Torokhov 19264104d13fSDmitry Torokhov static int __init aiptek_init(void) 19274104d13fSDmitry Torokhov { 19284104d13fSDmitry Torokhov int result = usb_register(&aiptek_driver); 19294104d13fSDmitry Torokhov if (result == 0) { 1930899ef6e7SGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 1931899ef6e7SGreg Kroah-Hartman DRIVER_DESC "\n"); 1932899ef6e7SGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_AUTHOR "\n"); 19334104d13fSDmitry Torokhov } 19344104d13fSDmitry Torokhov return result; 19354104d13fSDmitry Torokhov } 19364104d13fSDmitry Torokhov 19374104d13fSDmitry Torokhov static void __exit aiptek_exit(void) 19384104d13fSDmitry Torokhov { 19394104d13fSDmitry Torokhov usb_deregister(&aiptek_driver); 19404104d13fSDmitry Torokhov } 19414104d13fSDmitry Torokhov 19424104d13fSDmitry Torokhov MODULE_AUTHOR(DRIVER_AUTHOR); 19434104d13fSDmitry Torokhov MODULE_DESCRIPTION(DRIVER_DESC); 19444104d13fSDmitry Torokhov MODULE_LICENSE("GPL"); 19454104d13fSDmitry Torokhov 19464104d13fSDmitry Torokhov module_param(programmableDelay, int, 0); 19474104d13fSDmitry Torokhov MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming"); 19484104d13fSDmitry Torokhov module_param(jitterDelay, int, 0); 19494104d13fSDmitry Torokhov MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay"); 19504104d13fSDmitry Torokhov 19514104d13fSDmitry Torokhov module_init(aiptek_init); 19524104d13fSDmitry Torokhov module_exit(aiptek_exit); 1953