18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Native support for the Aiptek HyperPen USB Tablets 48c2ecf20Sopenharmony_ci * (4000U/5000U/6000U/8000U/12000U) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2001 Chris Atenasio <chris@crud.net> 78c2ecf20Sopenharmony_ci * Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * based on wacom.c by 108c2ecf20Sopenharmony_ci * Vojtech Pavlik <vojtech@suse.cz> 118c2ecf20Sopenharmony_ci * Andreas Bach Aaen <abach@stofanet.dk> 128c2ecf20Sopenharmony_ci * Clifford Wolf <clifford@clifford.at> 138c2ecf20Sopenharmony_ci * Sam Mosel <sam.mosel@computer.org> 148c2ecf20Sopenharmony_ci * James E. Blair <corvus@gnu.org> 158c2ecf20Sopenharmony_ci * Daniel Egger <egger@suse.de> 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Many thanks to Oliver Kuechemann for his support. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * ChangeLog: 208c2ecf20Sopenharmony_ci * v0.1 - Initial release 218c2ecf20Sopenharmony_ci * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley) 228c2ecf20Sopenharmony_ci * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002) 238c2ecf20Sopenharmony_ci * Released to Linux 2.4.19 and 2.5.x 248c2ecf20Sopenharmony_ci * v0.4 - Rewrote substantial portions of the code to deal with 258c2ecf20Sopenharmony_ci * corrected control sequences, timing, dynamic configuration, 268c2ecf20Sopenharmony_ci * support of 6000U - 12000U, procfs, and macro key support 278c2ecf20Sopenharmony_ci * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley) 288c2ecf20Sopenharmony_ci * v1.0 - Added support for diagnostic messages, count of messages 298c2ecf20Sopenharmony_ci * received from URB - Mar-8-2003, Bryan W. Headley 308c2ecf20Sopenharmony_ci * v1.1 - added support for tablet resolution, changed DV and proximity 318c2ecf20Sopenharmony_ci * some corrections - Jun-22-2003, martin schneebacher 328c2ecf20Sopenharmony_ci * - Added support for the sysfs interface, deprecating the 338c2ecf20Sopenharmony_ci * procfs interface for 2.5.x kernel. Also added support for 348c2ecf20Sopenharmony_ci * Wheel command. Bryan W. Headley July-15-2003. 358c2ecf20Sopenharmony_ci * v1.2 - Reworked jitter timer as a kernel thread. 368c2ecf20Sopenharmony_ci * Bryan W. Headley November-28-2003/Jan-10-2004. 378c2ecf20Sopenharmony_ci * v1.3 - Repaired issue of kernel thread going nuts on single-processor 388c2ecf20Sopenharmony_ci * machines, introduced programmableDelay as a command line 398c2ecf20Sopenharmony_ci * parameter. Feb 7 2004, Bryan W. Headley. 408c2ecf20Sopenharmony_ci * v1.4 - Re-wire jitter so it does not require a thread. Courtesy of 418c2ecf20Sopenharmony_ci * Rene van Paassen. Added reporting of physical pointer device 428c2ecf20Sopenharmony_ci * (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know 438c2ecf20Sopenharmony_ci * for reports 1, 6.) 448c2ecf20Sopenharmony_ci * what physical device reports for reports 1, 6.) Also enabled 458c2ecf20Sopenharmony_ci * MOUSE and LENS tool button modes. Renamed "rubber" to "eraser". 468c2ecf20Sopenharmony_ci * Feb 20, 2004, Bryan W. Headley. 478c2ecf20Sopenharmony_ci * v1.5 - Added previousJitterable, so we don't do jitter delay when the 488c2ecf20Sopenharmony_ci * user is holding a button down for periods of time. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * NOTE: 518c2ecf20Sopenharmony_ci * This kernel driver is augmented by the "Aiptek" XFree86 input 528c2ecf20Sopenharmony_ci * driver for your X server, as well as the Gaiptek GUI Front-end 538c2ecf20Sopenharmony_ci * "Tablet Manager". 548c2ecf20Sopenharmony_ci * These three products are highly interactive with one another, 558c2ecf20Sopenharmony_ci * so therefore it's easier to document them all as one subsystem. 568c2ecf20Sopenharmony_ci * Please visit the project's "home page", located at, 578c2ecf20Sopenharmony_ci * http://aiptektablet.sourceforge.net. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 618c2ecf20Sopenharmony_ci#include <linux/kernel.h> 628c2ecf20Sopenharmony_ci#include <linux/slab.h> 638c2ecf20Sopenharmony_ci#include <linux/module.h> 648c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 658c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 668c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Aiptek status packet: 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * (returned as Report 1 - relative coordinates from mouse and stylus) 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 748c2ecf20Sopenharmony_ci * byte0 0 0 0 0 0 0 0 1 758c2ecf20Sopenharmony_ci * byte1 0 0 0 0 0 BS2 BS Tip 768c2ecf20Sopenharmony_ci * byte2 X7 X6 X5 X4 X3 X2 X1 X0 778c2ecf20Sopenharmony_ci * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * (returned as Report 2 - absolute coordinates from the stylus) 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 828c2ecf20Sopenharmony_ci * byte0 0 0 0 0 0 0 1 0 838c2ecf20Sopenharmony_ci * byte1 X7 X6 X5 X4 X3 X2 X1 X0 848c2ecf20Sopenharmony_ci * byte2 X15 X14 X13 X12 X11 X10 X9 X8 858c2ecf20Sopenharmony_ci * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 868c2ecf20Sopenharmony_ci * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 878c2ecf20Sopenharmony_ci * byte5 * * * BS2 BS1 Tip IR DV 888c2ecf20Sopenharmony_ci * byte6 P7 P6 P5 P4 P3 P2 P1 P0 898c2ecf20Sopenharmony_ci * byte7 P15 P14 P13 P12 P11 P10 P9 P8 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * (returned as Report 3 - absolute coordinates from the mouse) 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 948c2ecf20Sopenharmony_ci * byte0 0 0 0 0 0 0 1 1 958c2ecf20Sopenharmony_ci * byte1 X7 X6 X5 X4 X3 X2 X1 X0 968c2ecf20Sopenharmony_ci * byte2 X15 X14 X13 X12 X11 X10 X9 X8 978c2ecf20Sopenharmony_ci * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 988c2ecf20Sopenharmony_ci * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 998c2ecf20Sopenharmony_ci * byte5 * * * BS2 BS1 Tip IR DV 1008c2ecf20Sopenharmony_ci * byte6 P7 P6 P5 P4 P3 P2 P1 P0 1018c2ecf20Sopenharmony_ci * byte7 P15 P14 P13 P12 P11 P10 P9 P8 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * (returned as Report 4 - macrokeys from the stylus) 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 1068c2ecf20Sopenharmony_ci * byte0 0 0 0 0 0 1 0 0 1078c2ecf20Sopenharmony_ci * byte1 0 0 0 BS2 BS Tip IR DV 1088c2ecf20Sopenharmony_ci * byte2 0 0 0 0 0 0 1 0 1098c2ecf20Sopenharmony_ci * byte3 0 0 0 K4 K3 K2 K1 K0 1108c2ecf20Sopenharmony_ci * byte4 P7 P6 P5 P4 P3 P2 P1 P0 1118c2ecf20Sopenharmony_ci * byte5 P15 P14 P13 P12 P11 P10 P9 P8 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * (returned as Report 5 - macrokeys from the mouse) 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 1168c2ecf20Sopenharmony_ci * byte0 0 0 0 0 0 1 0 1 1178c2ecf20Sopenharmony_ci * byte1 0 0 0 BS2 BS Tip IR DV 1188c2ecf20Sopenharmony_ci * byte2 0 0 0 0 0 0 1 0 1198c2ecf20Sopenharmony_ci * byte3 0 0 0 K4 K3 K2 K1 K0 1208c2ecf20Sopenharmony_ci * byte4 P7 P6 P5 P4 P3 P2 P1 P0 1218c2ecf20Sopenharmony_ci * byte5 P15 P14 P13 P12 P11 P10 P9 P8 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * IR: In Range = Proximity on 1248c2ecf20Sopenharmony_ci * DV = Data Valid 1258c2ecf20Sopenharmony_ci * BS = Barrel Switch (as in, macro keys) 1268c2ecf20Sopenharmony_ci * BS2 also referred to as Tablet Pick 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * Command Summary: 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Use report_type CONTROL (3) 1318c2ecf20Sopenharmony_ci * Use report_id 2 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * Command/Data Description Return Bytes Return Value 1348c2ecf20Sopenharmony_ci * 0x10/0x00 SwitchToMouse 0 1358c2ecf20Sopenharmony_ci * 0x10/0x01 SwitchToTablet 0 1368c2ecf20Sopenharmony_ci * 0x18/0x04 SetResolution 0 1378c2ecf20Sopenharmony_ci * 0x12/0xFF AutoGainOn 0 1388c2ecf20Sopenharmony_ci * 0x17/0x00 FilterOn 0 1398c2ecf20Sopenharmony_ci * 0x01/0x00 GetXExtension 2 MaxX 1408c2ecf20Sopenharmony_ci * 0x01/0x01 GetYExtension 2 MaxY 1418c2ecf20Sopenharmony_ci * 0x02/0x00 GetModelCode 2 ModelCode = LOBYTE 1428c2ecf20Sopenharmony_ci * 0x03/0x00 GetODMCode 2 ODMCode 1438c2ecf20Sopenharmony_ci * 0x08/0x00 GetPressureLevels 2 =512 1448c2ecf20Sopenharmony_ci * 0x04/0x00 GetFirmwareVersion 2 Firmware Version 1458c2ecf20Sopenharmony_ci * 0x11/0x02 EnableMacroKeys 0 1468c2ecf20Sopenharmony_ci * 1478c2ecf20Sopenharmony_ci * To initialize the tablet: 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * (1) Send Resolution500LPI (Command) 1508c2ecf20Sopenharmony_ci * (2) Query for Model code (Option Report) 1518c2ecf20Sopenharmony_ci * (3) Query for ODM code (Option Report) 1528c2ecf20Sopenharmony_ci * (4) Query for firmware (Option Report) 1538c2ecf20Sopenharmony_ci * (5) Query for GetXExtension (Option Report) 1548c2ecf20Sopenharmony_ci * (6) Query for GetYExtension (Option Report) 1558c2ecf20Sopenharmony_ci * (7) Query for GetPressureLevels (Option Report) 1568c2ecf20Sopenharmony_ci * (8) SwitchToTablet for Absolute coordinates, or 1578c2ecf20Sopenharmony_ci * SwitchToMouse for Relative coordinates (Command) 1588c2ecf20Sopenharmony_ci * (9) EnableMacroKeys (Command) 1598c2ecf20Sopenharmony_ci * (10) FilterOn (Command) 1608c2ecf20Sopenharmony_ci * (11) AutoGainOn (Command) 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * (Step 9 can be omitted, but you'll then have no function keys.) 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_AIPTEK 0x08ca 1668c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_KYE 0x0458 1678c2ecf20Sopenharmony_ci#define USB_REQ_GET_REPORT 0x01 1688c2ecf20Sopenharmony_ci#define USB_REQ_SET_REPORT 0x09 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* PointerMode codes 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci#define AIPTEK_POINTER_ONLY_MOUSE_MODE 0 1738c2ecf20Sopenharmony_ci#define AIPTEK_POINTER_ONLY_STYLUS_MODE 1 1748c2ecf20Sopenharmony_ci#define AIPTEK_POINTER_EITHER_MODE 2 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \ 1778c2ecf20Sopenharmony_ci (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \ 1788c2ecf20Sopenharmony_ci a == AIPTEK_POINTER_EITHER_MODE) 1798c2ecf20Sopenharmony_ci#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \ 1808c2ecf20Sopenharmony_ci (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \ 1818c2ecf20Sopenharmony_ci a == AIPTEK_POINTER_EITHER_MODE) 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* CoordinateMode code 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci#define AIPTEK_COORDINATE_RELATIVE_MODE 0 1868c2ecf20Sopenharmony_ci#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* XTilt and YTilt values 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci#define AIPTEK_TILT_MIN (-128) 1918c2ecf20Sopenharmony_ci#define AIPTEK_TILT_MAX 127 1928c2ecf20Sopenharmony_ci#define AIPTEK_TILT_DISABLE (-10101) 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Wheel values 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci#define AIPTEK_WHEEL_MIN 0 1978c2ecf20Sopenharmony_ci#define AIPTEK_WHEEL_MAX 1024 1988c2ecf20Sopenharmony_ci#define AIPTEK_WHEEL_DISABLE (-10101) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* ToolCode values, which BTW are 0x140 .. 0x14f 2018c2ecf20Sopenharmony_ci * We have things set up such that if the tool button has changed, 2028c2ecf20Sopenharmony_ci * the tools get reset. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci /* toolMode codes 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN 2078c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL 2088c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH 2098c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH 2108c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_ERASER_MODE BTN_TOOL_RUBBER 2118c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_MOUSE_MODE BTN_TOOL_MOUSE 2128c2ecf20Sopenharmony_ci#define AIPTEK_TOOL_BUTTON_LENS_MODE BTN_TOOL_LENS 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Diagnostic message codes 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_NA 0 2178c2ecf20Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1 2188c2ecf20Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 2198c2ecf20Sopenharmony_ci#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Time to wait (in ms) to help mask hand jittering 2228c2ecf20Sopenharmony_ci * when pressing the stylus buttons. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci#define AIPTEK_JITTER_DELAY_DEFAULT 50 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Time to wait (in ms) in-between sending the tablet 2278c2ecf20Sopenharmony_ci * a command and beginning the process of reading the return 2288c2ecf20Sopenharmony_ci * sequence from the tablet. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_25 25 2318c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_50 50 2328c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_100 100 2338c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_200 200 2348c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_300 300 2358c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_400 400 2368c2ecf20Sopenharmony_ci#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT AIPTEK_PROGRAMMABLE_DELAY_400 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Mouse button programming 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci#define AIPTEK_MOUSE_LEFT_BUTTON 0x04 2418c2ecf20Sopenharmony_ci#define AIPTEK_MOUSE_RIGHT_BUTTON 0x08 2428c2ecf20Sopenharmony_ci#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Stylus button programming 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci#define AIPTEK_STYLUS_LOWER_BUTTON 0x08 2478c2ecf20Sopenharmony_ci#define AIPTEK_STYLUS_UPPER_BUTTON 0x10 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Length of incoming packet from the tablet 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci#define AIPTEK_PACKET_LENGTH 8 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* We report in EV_MISC both the proximity and 2548c2ecf20Sopenharmony_ci * whether the report came from the stylus, tablet mouse 2558c2ecf20Sopenharmony_ci * or "unknown" -- Unknown when the tablet is in relative 2568c2ecf20Sopenharmony_ci * mode, because we only get report 1's. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci#define AIPTEK_REPORT_TOOL_UNKNOWN 0x10 2598c2ecf20Sopenharmony_ci#define AIPTEK_REPORT_TOOL_STYLUS 0x20 2608c2ecf20Sopenharmony_ci#define AIPTEK_REPORT_TOOL_MOUSE 0x40 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT; 2638c2ecf20Sopenharmony_cistatic int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistruct aiptek_features { 2668c2ecf20Sopenharmony_ci int odmCode; /* Tablet manufacturer code */ 2678c2ecf20Sopenharmony_ci int modelCode; /* Tablet model code (not unique) */ 2688c2ecf20Sopenharmony_ci int firmwareCode; /* prom/eeprom version */ 2698c2ecf20Sopenharmony_ci char usbPath[64 + 1]; /* device's physical usb path */ 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistruct aiptek_settings { 2738c2ecf20Sopenharmony_ci int pointerMode; /* stylus-, mouse-only or either */ 2748c2ecf20Sopenharmony_ci int coordinateMode; /* absolute/relative coords */ 2758c2ecf20Sopenharmony_ci int toolMode; /* pen, pencil, brush, etc. tool */ 2768c2ecf20Sopenharmony_ci int xTilt; /* synthetic xTilt amount */ 2778c2ecf20Sopenharmony_ci int yTilt; /* synthetic yTilt amount */ 2788c2ecf20Sopenharmony_ci int wheel; /* synthetic wheel amount */ 2798c2ecf20Sopenharmony_ci int stylusButtonUpper; /* stylus upper btn delivers... */ 2808c2ecf20Sopenharmony_ci int stylusButtonLower; /* stylus lower btn delivers... */ 2818c2ecf20Sopenharmony_ci int mouseButtonLeft; /* mouse left btn delivers... */ 2828c2ecf20Sopenharmony_ci int mouseButtonMiddle; /* mouse middle btn delivers... */ 2838c2ecf20Sopenharmony_ci int mouseButtonRight; /* mouse right btn delivers... */ 2848c2ecf20Sopenharmony_ci int programmableDelay; /* delay for tablet programming */ 2858c2ecf20Sopenharmony_ci int jitterDelay; /* delay for hand jittering */ 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistruct aiptek { 2898c2ecf20Sopenharmony_ci struct input_dev *inputdev; /* input device struct */ 2908c2ecf20Sopenharmony_ci struct usb_interface *intf; /* usb interface struct */ 2918c2ecf20Sopenharmony_ci struct urb *urb; /* urb for incoming reports */ 2928c2ecf20Sopenharmony_ci dma_addr_t data_dma; /* our dma stuffage */ 2938c2ecf20Sopenharmony_ci struct aiptek_features features; /* tablet's array of features */ 2948c2ecf20Sopenharmony_ci struct aiptek_settings curSetting; /* tablet's current programmable */ 2958c2ecf20Sopenharmony_ci struct aiptek_settings newSetting; /* ... and new param settings */ 2968c2ecf20Sopenharmony_ci unsigned int ifnum; /* interface number for IO */ 2978c2ecf20Sopenharmony_ci int diagnostic; /* tablet diagnostic codes */ 2988c2ecf20Sopenharmony_ci unsigned long eventCount; /* event count */ 2998c2ecf20Sopenharmony_ci int inDelay; /* jitter: in jitter delay? */ 3008c2ecf20Sopenharmony_ci unsigned long endDelay; /* jitter: time when delay ends */ 3018c2ecf20Sopenharmony_ci int previousJitterable; /* jitterable prev value */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci int lastMacro; /* macro key to reset */ 3048c2ecf20Sopenharmony_ci int previousToolMode; /* pen, pencil, brush, etc. tool */ 3058c2ecf20Sopenharmony_ci unsigned char *data; /* incoming packet data */ 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic const int eventTypes[] = { 3098c2ecf20Sopenharmony_ci EV_KEY, EV_ABS, EV_REL, EV_MSC, 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic const int absEvents[] = { 3138c2ecf20Sopenharmony_ci ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, 3148c2ecf20Sopenharmony_ci ABS_WHEEL, ABS_MISC, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic const int relEvents[] = { 3188c2ecf20Sopenharmony_ci REL_X, REL_Y, REL_WHEEL, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic const int buttonEvents[] = { 3228c2ecf20Sopenharmony_ci BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, 3238c2ecf20Sopenharmony_ci BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, 3248c2ecf20Sopenharmony_ci BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH, 3258c2ecf20Sopenharmony_ci BTN_STYLUS, BTN_STYLUS2, 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * Permit easy lookup of keyboard events to send, versus 3308c2ecf20Sopenharmony_ci * the bitmap which comes from the tablet. This hides the 3318c2ecf20Sopenharmony_ci * issue that the F_keys are not sequentially numbered. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_cistatic const int macroKeyEvents[] = { 3348c2ecf20Sopenharmony_ci KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, 3358c2ecf20Sopenharmony_ci KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, 3368c2ecf20Sopenharmony_ci KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, 3378c2ecf20Sopenharmony_ci KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, 3388c2ecf20Sopenharmony_ci KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, 3398c2ecf20Sopenharmony_ci KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/*********************************************************************** 3438c2ecf20Sopenharmony_ci * Map values to strings and back. Every map should have the following 3448c2ecf20Sopenharmony_ci * as its last element: { NULL, AIPTEK_INVALID_VALUE }. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci#define AIPTEK_INVALID_VALUE -1 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistruct aiptek_map { 3498c2ecf20Sopenharmony_ci const char *string; 3508c2ecf20Sopenharmony_ci int value; 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci const struct aiptek_map *p; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (str[count - 1] == '\n') 3588c2ecf20Sopenharmony_ci count--; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci for (p = map; p->string; p++) 3618c2ecf20Sopenharmony_ci if (!strncmp(str, p->string, count)) 3628c2ecf20Sopenharmony_ci return p->value; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return AIPTEK_INVALID_VALUE; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const char *map_val_to_str(const struct aiptek_map *map, int val) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci const struct aiptek_map *p; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci for (p = map; p->value != AIPTEK_INVALID_VALUE; p++) 3728c2ecf20Sopenharmony_ci if (val == p->value) 3738c2ecf20Sopenharmony_ci return p->string; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return "unknown"; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/*********************************************************************** 3798c2ecf20Sopenharmony_ci * aiptek_irq can receive one of six potential reports. 3808c2ecf20Sopenharmony_ci * The documentation for each is in the body of the function. 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * The tablet reports on several attributes per invocation of 3838c2ecf20Sopenharmony_ci * aiptek_irq. Because the Linux Input Event system allows the 3848c2ecf20Sopenharmony_ci * transmission of ONE attribute per input_report_xxx() call, 3858c2ecf20Sopenharmony_ci * collation has to be done on the other end to reconstitute 3868c2ecf20Sopenharmony_ci * a complete tablet report. Further, the number of Input Event reports 3878c2ecf20Sopenharmony_ci * submitted varies, depending on what USB report type, and circumstance. 3888c2ecf20Sopenharmony_ci * To deal with this, EV_MSC is used to indicate an 'end-of-report' 3898c2ecf20Sopenharmony_ci * message. This has been an undocumented convention understood by the kernel 3908c2ecf20Sopenharmony_ci * tablet driver and clients such as gpm and XFree86's tablet drivers. 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Of the information received from the tablet, the one piece I 3938c2ecf20Sopenharmony_ci * cannot transmit is the proximity bit (without resorting to an EV_MSC 3948c2ecf20Sopenharmony_ci * convention above.) I therefore have taken over REL_MISC and ABS_MISC 3958c2ecf20Sopenharmony_ci * (for relative and absolute reports, respectively) for communicating 3968c2ecf20Sopenharmony_ci * Proximity. Why two events? I thought it interesting to know if the 3978c2ecf20Sopenharmony_ci * Proximity event occurred while the tablet was in absolute or relative 3988c2ecf20Sopenharmony_ci * mode. 3998c2ecf20Sopenharmony_ci * Update: REL_MISC proved not to be such a good idea. With REL_MISC you 4008c2ecf20Sopenharmony_ci * get an event transmitted each time. ABS_MISC works better, since it 4018c2ecf20Sopenharmony_ci * can be set and re-set. Thus, only using ABS_MISC from now on. 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * Other tablets use the notion of a certain minimum stylus pressure 4048c2ecf20Sopenharmony_ci * to infer proximity. While that could have been done, that is yet 4058c2ecf20Sopenharmony_ci * another 'by convention' behavior, the documentation for which 4068c2ecf20Sopenharmony_ci * would be spread between two (or more) pieces of software. 4078c2ecf20Sopenharmony_ci * 4088c2ecf20Sopenharmony_ci * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and 4098c2ecf20Sopenharmony_ci * replaced with the input_sync() method (which emits EV_SYN.) 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void aiptek_irq(struct urb *urb) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct aiptek *aiptek = urb->context; 4158c2ecf20Sopenharmony_ci unsigned char *data = aiptek->data; 4168c2ecf20Sopenharmony_ci struct input_dev *inputdev = aiptek->inputdev; 4178c2ecf20Sopenharmony_ci struct usb_interface *intf = aiptek->intf; 4188c2ecf20Sopenharmony_ci int jitterable = 0; 4198c2ecf20Sopenharmony_ci int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci switch (urb->status) { 4228c2ecf20Sopenharmony_ci case 0: 4238c2ecf20Sopenharmony_ci /* Success */ 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci case -ECONNRESET: 4278c2ecf20Sopenharmony_ci case -ENOENT: 4288c2ecf20Sopenharmony_ci case -ESHUTDOWN: 4298c2ecf20Sopenharmony_ci /* This urb is terminated, clean up */ 4308c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "%s - urb shutting down with status: %d\n", 4318c2ecf20Sopenharmony_ci __func__, urb->status); 4328c2ecf20Sopenharmony_ci return; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci default: 4358c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "%s - nonzero urb status received: %d\n", 4368c2ecf20Sopenharmony_ci __func__, urb->status); 4378c2ecf20Sopenharmony_ci goto exit; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* See if we are in a delay loop -- throw out report if true. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) { 4438c2ecf20Sopenharmony_ci goto exit; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci aiptek->inDelay = 0; 4478c2ecf20Sopenharmony_ci aiptek->eventCount++; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Report 1 delivers relative coordinates with either a stylus 4508c2ecf20Sopenharmony_ci * or the mouse. You do not know, however, which input 4518c2ecf20Sopenharmony_ci * tool generated the event. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (data[0] == 1) { 4548c2ecf20Sopenharmony_ci if (aiptek->curSetting.coordinateMode == 4558c2ecf20Sopenharmony_ci AIPTEK_COORDINATE_ABSOLUTE_MODE) { 4568c2ecf20Sopenharmony_ci aiptek->diagnostic = 4578c2ecf20Sopenharmony_ci AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE; 4588c2ecf20Sopenharmony_ci } else { 4598c2ecf20Sopenharmony_ci x = (signed char) data[2]; 4608c2ecf20Sopenharmony_ci y = (signed char) data[3]; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* jitterable keeps track of whether any button has been pressed. 4638c2ecf20Sopenharmony_ci * We're also using it to remap the physical mouse button mask 4648c2ecf20Sopenharmony_ci * to pseudo-settings. (We don't specifically care about it's 4658c2ecf20Sopenharmony_ci * value after moving/transposing mouse button bitmasks, except 4668c2ecf20Sopenharmony_ci * that a non-zero value indicates that one or more 4678c2ecf20Sopenharmony_ci * mouse button was pressed.) 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_ci jitterable = data[1] & 0x07; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0; 4728c2ecf20Sopenharmony_ci right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0; 4738c2ecf20Sopenharmony_ci middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_LEFT, left); 4768c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_MIDDLE, middle); 4778c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_RIGHT, right); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_MISC, 4808c2ecf20Sopenharmony_ci 1 | AIPTEK_REPORT_TOOL_UNKNOWN); 4818c2ecf20Sopenharmony_ci input_report_rel(inputdev, REL_X, x); 4828c2ecf20Sopenharmony_ci input_report_rel(inputdev, REL_Y, y); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Wheel support is in the form of a single-event 4858c2ecf20Sopenharmony_ci * firing. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { 4888c2ecf20Sopenharmony_ci input_report_rel(inputdev, REL_WHEEL, 4898c2ecf20Sopenharmony_ci aiptek->curSetting.wheel); 4908c2ecf20Sopenharmony_ci aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci if (aiptek->lastMacro != -1) { 4938c2ecf20Sopenharmony_ci input_report_key(inputdev, 4948c2ecf20Sopenharmony_ci macroKeyEvents[aiptek->lastMacro], 0); 4958c2ecf20Sopenharmony_ci aiptek->lastMacro = -1; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci input_sync(inputdev); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci /* Report 2 is delivered only by the stylus, and delivers 5018c2ecf20Sopenharmony_ci * absolute coordinates. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci else if (data[0] == 2) { 5048c2ecf20Sopenharmony_ci if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { 5058c2ecf20Sopenharmony_ci aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; 5068c2ecf20Sopenharmony_ci } else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE 5078c2ecf20Sopenharmony_ci (aiptek->curSetting.pointerMode)) { 5088c2ecf20Sopenharmony_ci aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; 5098c2ecf20Sopenharmony_ci } else { 5108c2ecf20Sopenharmony_ci x = get_unaligned_le16(data + 1); 5118c2ecf20Sopenharmony_ci y = get_unaligned_le16(data + 3); 5128c2ecf20Sopenharmony_ci z = get_unaligned_le16(data + 6); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci dv = (data[5] & 0x01) != 0 ? 1 : 0; 5158c2ecf20Sopenharmony_ci p = (data[5] & 0x02) != 0 ? 1 : 0; 5168c2ecf20Sopenharmony_ci tip = (data[5] & 0x04) != 0 ? 1 : 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Use jitterable to re-arrange button masks 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci jitterable = data[5] & 0x18; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; 5238c2ecf20Sopenharmony_ci pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* dv indicates 'data valid' (e.g., the tablet is in sync 5268c2ecf20Sopenharmony_ci * and has delivered a "correct" report) We will ignore 5278c2ecf20Sopenharmony_ci * all 'bad' reports... 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ci if (dv != 0) { 5308c2ecf20Sopenharmony_ci /* If the selected tool changed, reset the old 5318c2ecf20Sopenharmony_ci * tool key, and set the new one. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ci if (aiptek->previousToolMode != 5348c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode) { 5358c2ecf20Sopenharmony_ci input_report_key(inputdev, 5368c2ecf20Sopenharmony_ci aiptek->previousToolMode, 0); 5378c2ecf20Sopenharmony_ci input_report_key(inputdev, 5388c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode, 5398c2ecf20Sopenharmony_ci 1); 5408c2ecf20Sopenharmony_ci aiptek->previousToolMode = 5418c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (p != 0) { 5458c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_X, x); 5468c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_Y, y); 5478c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_PRESSURE, z); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_TOUCH, tip); 5508c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_STYLUS, bs); 5518c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_STYLUS2, pck); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (aiptek->curSetting.xTilt != 5548c2ecf20Sopenharmony_ci AIPTEK_TILT_DISABLE) { 5558c2ecf20Sopenharmony_ci input_report_abs(inputdev, 5568c2ecf20Sopenharmony_ci ABS_TILT_X, 5578c2ecf20Sopenharmony_ci aiptek->curSetting.xTilt); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) { 5608c2ecf20Sopenharmony_ci input_report_abs(inputdev, 5618c2ecf20Sopenharmony_ci ABS_TILT_Y, 5628c2ecf20Sopenharmony_ci aiptek->curSetting.yTilt); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Wheel support is in the form of a single-event 5668c2ecf20Sopenharmony_ci * firing. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci if (aiptek->curSetting.wheel != 5698c2ecf20Sopenharmony_ci AIPTEK_WHEEL_DISABLE) { 5708c2ecf20Sopenharmony_ci input_report_abs(inputdev, 5718c2ecf20Sopenharmony_ci ABS_WHEEL, 5728c2ecf20Sopenharmony_ci aiptek->curSetting.wheel); 5738c2ecf20Sopenharmony_ci aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS); 5778c2ecf20Sopenharmony_ci if (aiptek->lastMacro != -1) { 5788c2ecf20Sopenharmony_ci input_report_key(inputdev, 5798c2ecf20Sopenharmony_ci macroKeyEvents[aiptek->lastMacro], 0); 5808c2ecf20Sopenharmony_ci aiptek->lastMacro = -1; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci input_sync(inputdev); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci /* Report 3's come from the mouse in absolute mode. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci else if (data[0] == 3) { 5898c2ecf20Sopenharmony_ci if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { 5908c2ecf20Sopenharmony_ci aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; 5918c2ecf20Sopenharmony_ci } else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE 5928c2ecf20Sopenharmony_ci (aiptek->curSetting.pointerMode)) { 5938c2ecf20Sopenharmony_ci aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; 5948c2ecf20Sopenharmony_ci } else { 5958c2ecf20Sopenharmony_ci x = get_unaligned_le16(data + 1); 5968c2ecf20Sopenharmony_ci y = get_unaligned_le16(data + 3); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci jitterable = data[5] & 0x1c; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci dv = (data[5] & 0x01) != 0 ? 1 : 0; 6018c2ecf20Sopenharmony_ci p = (data[5] & 0x02) != 0 ? 1 : 0; 6028c2ecf20Sopenharmony_ci left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; 6038c2ecf20Sopenharmony_ci right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; 6048c2ecf20Sopenharmony_ci middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (dv != 0) { 6078c2ecf20Sopenharmony_ci /* If the selected tool changed, reset the old 6088c2ecf20Sopenharmony_ci * tool key, and set the new one. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci if (aiptek->previousToolMode != 6118c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode) { 6128c2ecf20Sopenharmony_ci input_report_key(inputdev, 6138c2ecf20Sopenharmony_ci aiptek->previousToolMode, 0); 6148c2ecf20Sopenharmony_ci input_report_key(inputdev, 6158c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode, 6168c2ecf20Sopenharmony_ci 1); 6178c2ecf20Sopenharmony_ci aiptek->previousToolMode = 6188c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (p != 0) { 6228c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_X, x); 6238c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_Y, y); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_LEFT, left); 6268c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_MIDDLE, middle); 6278c2ecf20Sopenharmony_ci input_report_key(inputdev, BTN_RIGHT, right); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Wheel support is in the form of a single-event 6308c2ecf20Sopenharmony_ci * firing. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { 6338c2ecf20Sopenharmony_ci input_report_abs(inputdev, 6348c2ecf20Sopenharmony_ci ABS_WHEEL, 6358c2ecf20Sopenharmony_ci aiptek->curSetting.wheel); 6368c2ecf20Sopenharmony_ci aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); 6408c2ecf20Sopenharmony_ci if (aiptek->lastMacro != -1) { 6418c2ecf20Sopenharmony_ci input_report_key(inputdev, 6428c2ecf20Sopenharmony_ci macroKeyEvents[aiptek->lastMacro], 0); 6438c2ecf20Sopenharmony_ci aiptek->lastMacro = -1; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci input_sync(inputdev); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci /* Report 4s come from the macro keys when pressed by stylus 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci else if (data[0] == 4) { 6528c2ecf20Sopenharmony_ci jitterable = data[1] & 0x18; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci dv = (data[1] & 0x01) != 0 ? 1 : 0; 6558c2ecf20Sopenharmony_ci p = (data[1] & 0x02) != 0 ? 1 : 0; 6568c2ecf20Sopenharmony_ci tip = (data[1] & 0x04) != 0 ? 1 : 0; 6578c2ecf20Sopenharmony_ci bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; 6588c2ecf20Sopenharmony_ci pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; 6618c2ecf20Sopenharmony_ci z = get_unaligned_le16(data + 4); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (dv) { 6648c2ecf20Sopenharmony_ci /* If the selected tool changed, reset the old 6658c2ecf20Sopenharmony_ci * tool key, and set the new one. 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ci if (aiptek->previousToolMode != 6688c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode) { 6698c2ecf20Sopenharmony_ci input_report_key(inputdev, 6708c2ecf20Sopenharmony_ci aiptek->previousToolMode, 0); 6718c2ecf20Sopenharmony_ci input_report_key(inputdev, 6728c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode, 6738c2ecf20Sopenharmony_ci 1); 6748c2ecf20Sopenharmony_ci aiptek->previousToolMode = 6758c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { 6808c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); 6818c2ecf20Sopenharmony_ci aiptek->lastMacro = -1; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (macro != -1 && macro != aiptek->lastMacro) { 6858c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[macro], 1); 6868c2ecf20Sopenharmony_ci aiptek->lastMacro = macro; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_MISC, 6898c2ecf20Sopenharmony_ci p | AIPTEK_REPORT_TOOL_STYLUS); 6908c2ecf20Sopenharmony_ci input_sync(inputdev); 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci /* Report 5s come from the macro keys when pressed by mouse 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci else if (data[0] == 5) { 6958c2ecf20Sopenharmony_ci jitterable = data[1] & 0x1c; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci dv = (data[1] & 0x01) != 0 ? 1 : 0; 6988c2ecf20Sopenharmony_ci p = (data[1] & 0x02) != 0 ? 1 : 0; 6998c2ecf20Sopenharmony_ci left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; 7008c2ecf20Sopenharmony_ci right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; 7018c2ecf20Sopenharmony_ci middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; 7028c2ecf20Sopenharmony_ci macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (dv) { 7058c2ecf20Sopenharmony_ci /* If the selected tool changed, reset the old 7068c2ecf20Sopenharmony_ci * tool key, and set the new one. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ci if (aiptek->previousToolMode != 7098c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode) { 7108c2ecf20Sopenharmony_ci input_report_key(inputdev, 7118c2ecf20Sopenharmony_ci aiptek->previousToolMode, 0); 7128c2ecf20Sopenharmony_ci input_report_key(inputdev, 7138c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode, 1); 7148c2ecf20Sopenharmony_ci aiptek->previousToolMode = aiptek->curSetting.toolMode; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { 7198c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); 7208c2ecf20Sopenharmony_ci aiptek->lastMacro = -1; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (macro != -1 && macro != aiptek->lastMacro) { 7248c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[macro], 1); 7258c2ecf20Sopenharmony_ci aiptek->lastMacro = macro; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_MISC, 7298c2ecf20Sopenharmony_ci p | AIPTEK_REPORT_TOOL_MOUSE); 7308c2ecf20Sopenharmony_ci input_sync(inputdev); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci /* We have no idea which tool can generate a report 6. Theoretically, 7338c2ecf20Sopenharmony_ci * neither need to, having been given reports 4 & 5 for such use. 7348c2ecf20Sopenharmony_ci * However, report 6 is the 'official-looking' report for macroKeys; 7358c2ecf20Sopenharmony_ci * reports 4 & 5 supposively are used to support unnamed, unknown 7368c2ecf20Sopenharmony_ci * hat switches (which just so happen to be the macroKeys.) 7378c2ecf20Sopenharmony_ci */ 7388c2ecf20Sopenharmony_ci else if (data[0] == 6) { 7398c2ecf20Sopenharmony_ci macro = get_unaligned_le16(data + 1); 7408c2ecf20Sopenharmony_ci if (macro > 0) { 7418c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[macro - 1], 7428c2ecf20Sopenharmony_ci 0); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci if (macro < 25) { 7458c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[macro + 1], 7468c2ecf20Sopenharmony_ci 0); 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* If the selected tool changed, reset the old 7508c2ecf20Sopenharmony_ci tool key, and set the new one. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_ci if (aiptek->previousToolMode != 7538c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode) { 7548c2ecf20Sopenharmony_ci input_report_key(inputdev, 7558c2ecf20Sopenharmony_ci aiptek->previousToolMode, 0); 7568c2ecf20Sopenharmony_ci input_report_key(inputdev, 7578c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode, 7588c2ecf20Sopenharmony_ci 1); 7598c2ecf20Sopenharmony_ci aiptek->previousToolMode = 7608c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci input_report_key(inputdev, macroKeyEvents[macro], 1); 7648c2ecf20Sopenharmony_ci input_report_abs(inputdev, ABS_MISC, 7658c2ecf20Sopenharmony_ci 1 | AIPTEK_REPORT_TOOL_UNKNOWN); 7668c2ecf20Sopenharmony_ci input_sync(inputdev); 7678c2ecf20Sopenharmony_ci } else { 7688c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "Unknown report %d\n", data[0]); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* Jitter may occur when the user presses a button on the stlyus 7728c2ecf20Sopenharmony_ci * or the mouse. What we do to prevent that is wait 'x' milliseconds 7738c2ecf20Sopenharmony_ci * following a 'jitterable' event, which should give the hand some time 7748c2ecf20Sopenharmony_ci * stabilize itself. 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * We just introduced aiptek->previousJitterable to carry forth the 7778c2ecf20Sopenharmony_ci * notion that jitter occurs when the button state changes from on to off: 7788c2ecf20Sopenharmony_ci * a person drawing, holding a button down is not subject to jittering. 7798c2ecf20Sopenharmony_ci * With that in mind, changing from upper button depressed to lower button 7808c2ecf20Sopenharmony_ci * WILL transition through a jitter delay. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (aiptek->previousJitterable != jitterable && 7848c2ecf20Sopenharmony_ci aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) { 7858c2ecf20Sopenharmony_ci aiptek->endDelay = jiffies + 7868c2ecf20Sopenharmony_ci ((aiptek->curSetting.jitterDelay * HZ) / 1000); 7878c2ecf20Sopenharmony_ci aiptek->inDelay = 1; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci aiptek->previousJitterable = jitterable; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ciexit: 7928c2ecf20Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 7938c2ecf20Sopenharmony_ci if (retval != 0) { 7948c2ecf20Sopenharmony_ci dev_err(&intf->dev, 7958c2ecf20Sopenharmony_ci "%s - usb_submit_urb failed with result %d\n", 7968c2ecf20Sopenharmony_ci __func__, retval); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/*********************************************************************** 8018c2ecf20Sopenharmony_ci * These are the USB id's known so far. We do not identify them to 8028c2ecf20Sopenharmony_ci * specific Aiptek model numbers, because there has been overlaps, 8038c2ecf20Sopenharmony_ci * use, and reuse of id's in existing models. Certain models have 8048c2ecf20Sopenharmony_ci * been known to use more than one ID, indicative perhaps of 8058c2ecf20Sopenharmony_ci * manufacturing revisions. In any event, we consider these 8068c2ecf20Sopenharmony_ci * IDs to not be model-specific nor unique. 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_cistatic const struct usb_device_id aiptek_ids[] = { 8098c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)}, 8108c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)}, 8118c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)}, 8128c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)}, 8138c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)}, 8148c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)}, 8158c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)}, 8168c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)}, 8178c2ecf20Sopenharmony_ci {} 8188c2ecf20Sopenharmony_ci}; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, aiptek_ids); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/*********************************************************************** 8238c2ecf20Sopenharmony_ci * Open an instance of the tablet driver. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_cistatic int aiptek_open(struct input_dev *inputdev) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct aiptek *aiptek = input_get_drvdata(inputdev); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci aiptek->urb->dev = interface_to_usbdev(aiptek->intf); 8308c2ecf20Sopenharmony_ci if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) 8318c2ecf20Sopenharmony_ci return -EIO; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/*********************************************************************** 8378c2ecf20Sopenharmony_ci * Close an instance of the tablet driver. 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_cistatic void aiptek_close(struct input_dev *inputdev) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct aiptek *aiptek = input_get_drvdata(inputdev); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci usb_kill_urb(aiptek->urb); 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/*********************************************************************** 8478c2ecf20Sopenharmony_ci * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, 8488c2ecf20Sopenharmony_ci * where they were known as usb_set_report and usb_get_report. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_cistatic int 8518c2ecf20Sopenharmony_ciaiptek_set_report(struct aiptek *aiptek, 8528c2ecf20Sopenharmony_ci unsigned char report_type, 8538c2ecf20Sopenharmony_ci unsigned char report_id, void *buffer, int size) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(aiptek->intf); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return usb_control_msg(udev, 8588c2ecf20Sopenharmony_ci usb_sndctrlpipe(udev, 0), 8598c2ecf20Sopenharmony_ci USB_REQ_SET_REPORT, 8608c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | 8618c2ecf20Sopenharmony_ci USB_DIR_OUT, (report_type << 8) + report_id, 8628c2ecf20Sopenharmony_ci aiptek->ifnum, buffer, size, 5000); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic int 8668c2ecf20Sopenharmony_ciaiptek_get_report(struct aiptek *aiptek, 8678c2ecf20Sopenharmony_ci unsigned char report_type, 8688c2ecf20Sopenharmony_ci unsigned char report_id, void *buffer, int size) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(aiptek->intf); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return usb_control_msg(udev, 8738c2ecf20Sopenharmony_ci usb_rcvctrlpipe(udev, 0), 8748c2ecf20Sopenharmony_ci USB_REQ_GET_REPORT, 8758c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE | 8768c2ecf20Sopenharmony_ci USB_DIR_IN, (report_type << 8) + report_id, 8778c2ecf20Sopenharmony_ci aiptek->ifnum, buffer, size, 5000); 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/*********************************************************************** 8818c2ecf20Sopenharmony_ci * Send a command to the tablet. 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_cistatic int 8848c2ecf20Sopenharmony_ciaiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci const int sizeof_buf = 3 * sizeof(u8); 8878c2ecf20Sopenharmony_ci int ret; 8888c2ecf20Sopenharmony_ci u8 *buf; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci buf = kmalloc(sizeof_buf, GFP_KERNEL); 8918c2ecf20Sopenharmony_ci if (!buf) 8928c2ecf20Sopenharmony_ci return -ENOMEM; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci buf[0] = 2; 8958c2ecf20Sopenharmony_ci buf[1] = command; 8968c2ecf20Sopenharmony_ci buf[2] = data; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if ((ret = 8998c2ecf20Sopenharmony_ci aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { 9008c2ecf20Sopenharmony_ci dev_dbg(&aiptek->intf->dev, 9018c2ecf20Sopenharmony_ci "aiptek_program: failed, tried to send: 0x%02x 0x%02x\n", 9028c2ecf20Sopenharmony_ci command, data); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci kfree(buf); 9058c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci/*********************************************************************** 9098c2ecf20Sopenharmony_ci * Retrieve information from the tablet. Querying info is defined as first 9108c2ecf20Sopenharmony_ci * sending the {command,data} sequence as a command, followed by a wait 9118c2ecf20Sopenharmony_ci * (aka, "programmaticDelay") and then a "read" request. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_cistatic int 9148c2ecf20Sopenharmony_ciaiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci const int sizeof_buf = 3 * sizeof(u8); 9178c2ecf20Sopenharmony_ci int ret; 9188c2ecf20Sopenharmony_ci u8 *buf; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci buf = kmalloc(sizeof_buf, GFP_KERNEL); 9218c2ecf20Sopenharmony_ci if (!buf) 9228c2ecf20Sopenharmony_ci return -ENOMEM; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci buf[0] = 2; 9258c2ecf20Sopenharmony_ci buf[1] = command; 9268c2ecf20Sopenharmony_ci buf[2] = data; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (aiptek_command(aiptek, command, data) != 0) { 9298c2ecf20Sopenharmony_ci kfree(buf); 9308c2ecf20Sopenharmony_ci return -EIO; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci msleep(aiptek->curSetting.programmableDelay); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if ((ret = 9358c2ecf20Sopenharmony_ci aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { 9368c2ecf20Sopenharmony_ci dev_dbg(&aiptek->intf->dev, 9378c2ecf20Sopenharmony_ci "aiptek_query failed: returned 0x%02x 0x%02x 0x%02x\n", 9388c2ecf20Sopenharmony_ci buf[0], buf[1], buf[2]); 9398c2ecf20Sopenharmony_ci ret = -EIO; 9408c2ecf20Sopenharmony_ci } else { 9418c2ecf20Sopenharmony_ci ret = get_unaligned_le16(buf + 1); 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci kfree(buf); 9448c2ecf20Sopenharmony_ci return ret; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci/*********************************************************************** 9488c2ecf20Sopenharmony_ci * Program the tablet into either absolute or relative mode. 9498c2ecf20Sopenharmony_ci * We also get information about the tablet's size. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_cistatic int aiptek_program_tablet(struct aiptek *aiptek) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci int ret; 9548c2ecf20Sopenharmony_ci /* Execute Resolution500LPI */ 9558c2ecf20Sopenharmony_ci if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0) 9568c2ecf20Sopenharmony_ci return ret; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci /* Query getModelCode */ 9598c2ecf20Sopenharmony_ci if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0) 9608c2ecf20Sopenharmony_ci return ret; 9618c2ecf20Sopenharmony_ci aiptek->features.modelCode = ret & 0xff; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* Query getODMCode */ 9648c2ecf20Sopenharmony_ci if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0) 9658c2ecf20Sopenharmony_ci return ret; 9668c2ecf20Sopenharmony_ci aiptek->features.odmCode = ret; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* Query getFirmwareCode */ 9698c2ecf20Sopenharmony_ci if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0) 9708c2ecf20Sopenharmony_ci return ret; 9718c2ecf20Sopenharmony_ci aiptek->features.firmwareCode = ret; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* Query getXextension */ 9748c2ecf20Sopenharmony_ci if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0) 9758c2ecf20Sopenharmony_ci return ret; 9768c2ecf20Sopenharmony_ci input_set_abs_params(aiptek->inputdev, ABS_X, 0, ret - 1, 0, 0); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* Query getYextension */ 9798c2ecf20Sopenharmony_ci if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0) 9808c2ecf20Sopenharmony_ci return ret; 9818c2ecf20Sopenharmony_ci input_set_abs_params(aiptek->inputdev, ABS_Y, 0, ret - 1, 0, 0); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* Query getPressureLevels */ 9848c2ecf20Sopenharmony_ci if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0) 9858c2ecf20Sopenharmony_ci return ret; 9868c2ecf20Sopenharmony_ci input_set_abs_params(aiptek->inputdev, ABS_PRESSURE, 0, ret - 1, 0, 0); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* Depending on whether we are in absolute or relative mode, we will 9898c2ecf20Sopenharmony_ci * do a switchToTablet(absolute) or switchToMouse(relative) command. 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_ci if (aiptek->curSetting.coordinateMode == 9928c2ecf20Sopenharmony_ci AIPTEK_COORDINATE_ABSOLUTE_MODE) { 9938c2ecf20Sopenharmony_ci /* Execute switchToTablet */ 9948c2ecf20Sopenharmony_ci if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) { 9958c2ecf20Sopenharmony_ci return ret; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci } else { 9988c2ecf20Sopenharmony_ci /* Execute switchToMouse */ 9998c2ecf20Sopenharmony_ci if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) { 10008c2ecf20Sopenharmony_ci return ret; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* Enable the macro keys */ 10058c2ecf20Sopenharmony_ci if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0) 10068c2ecf20Sopenharmony_ci return ret; 10078c2ecf20Sopenharmony_ci#if 0 10088c2ecf20Sopenharmony_ci /* Execute FilterOn */ 10098c2ecf20Sopenharmony_ci if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0) 10108c2ecf20Sopenharmony_ci return ret; 10118c2ecf20Sopenharmony_ci#endif 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* Execute AutoGainOn */ 10148c2ecf20Sopenharmony_ci if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0) 10158c2ecf20Sopenharmony_ci return ret; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* Reset the eventCount, so we track events from last (re)programming 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_ci aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA; 10208c2ecf20Sopenharmony_ci aiptek->eventCount = 0; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci return 0; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci/*********************************************************************** 10268c2ecf20Sopenharmony_ci * Sysfs functions. Sysfs prefers that individually-tunable parameters 10278c2ecf20Sopenharmony_ci * exist in their separate pseudo-files. Summary data that is immutable 10288c2ecf20Sopenharmony_ci * may exist in a singular file so long as you don't define a writeable 10298c2ecf20Sopenharmony_ci * interface. 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci/*********************************************************************** 10338c2ecf20Sopenharmony_ci * support the 'size' file -- display support 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_cistatic ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%dx%d\n", 10408c2ecf20Sopenharmony_ci input_abs_get_max(aiptek->inputdev, ABS_X) + 1, 10418c2ecf20Sopenharmony_ci input_abs_get_max(aiptek->inputdev, ABS_Y) + 1); 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci/* These structs define the sysfs files, param #1 is the name of the 10458c2ecf20Sopenharmony_ci * file, param 2 is the file permissions, param 3 & 4 are to the 10468c2ecf20Sopenharmony_ci * output generator and input parser routines. Absence of a routine is 10478c2ecf20Sopenharmony_ci * permitted -- it only means can't either 'cat' the file, or send data 10488c2ecf20Sopenharmony_ci * to it. 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_cistatic DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci/*********************************************************************** 10538c2ecf20Sopenharmony_ci * support routines for the 'pointer_mode' file. Note that this file 10548c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 10558c2ecf20Sopenharmony_ci */ 10568c2ecf20Sopenharmony_cistatic struct aiptek_map pointer_mode_map[] = { 10578c2ecf20Sopenharmony_ci { "stylus", AIPTEK_POINTER_ONLY_STYLUS_MODE }, 10588c2ecf20Sopenharmony_ci { "mouse", AIPTEK_POINTER_ONLY_MOUSE_MODE }, 10598c2ecf20Sopenharmony_ci { "either", AIPTEK_POINTER_EITHER_MODE }, 10608c2ecf20Sopenharmony_ci { NULL, AIPTEK_INVALID_VALUE } 10618c2ecf20Sopenharmony_ci}; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 10688c2ecf20Sopenharmony_ci map_val_to_str(pointer_mode_map, 10698c2ecf20Sopenharmony_ci aiptek->curSetting.pointerMode)); 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic ssize_t 10738c2ecf20Sopenharmony_cistore_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 10768c2ecf20Sopenharmony_ci int new_mode = map_str_to_val(pointer_mode_map, buf, count); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (new_mode == AIPTEK_INVALID_VALUE) 10798c2ecf20Sopenharmony_ci return -EINVAL; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci aiptek->newSetting.pointerMode = new_mode; 10828c2ecf20Sopenharmony_ci return count; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic DEVICE_ATTR(pointer_mode, 10868c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 10878c2ecf20Sopenharmony_ci show_tabletPointerMode, store_tabletPointerMode); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/*********************************************************************** 10908c2ecf20Sopenharmony_ci * support routines for the 'coordinate_mode' file. Note that this file 10918c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 10928c2ecf20Sopenharmony_ci */ 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic struct aiptek_map coordinate_mode_map[] = { 10958c2ecf20Sopenharmony_ci { "absolute", AIPTEK_COORDINATE_ABSOLUTE_MODE }, 10968c2ecf20Sopenharmony_ci { "relative", AIPTEK_COORDINATE_RELATIVE_MODE }, 10978c2ecf20Sopenharmony_ci { NULL, AIPTEK_INVALID_VALUE } 10988c2ecf20Sopenharmony_ci}; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 11058c2ecf20Sopenharmony_ci map_val_to_str(coordinate_mode_map, 11068c2ecf20Sopenharmony_ci aiptek->curSetting.coordinateMode)); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic ssize_t 11108c2ecf20Sopenharmony_cistore_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 11138c2ecf20Sopenharmony_ci int new_mode = map_str_to_val(coordinate_mode_map, buf, count); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (new_mode == AIPTEK_INVALID_VALUE) 11168c2ecf20Sopenharmony_ci return -EINVAL; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci aiptek->newSetting.coordinateMode = new_mode; 11198c2ecf20Sopenharmony_ci return count; 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(coordinate_mode, 11238c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 11248c2ecf20Sopenharmony_ci show_tabletCoordinateMode, store_tabletCoordinateMode); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci/*********************************************************************** 11278c2ecf20Sopenharmony_ci * support routines for the 'tool_mode' file. Note that this file 11288c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic struct aiptek_map tool_mode_map[] = { 11328c2ecf20Sopenharmony_ci { "mouse", AIPTEK_TOOL_BUTTON_MOUSE_MODE }, 11338c2ecf20Sopenharmony_ci { "eraser", AIPTEK_TOOL_BUTTON_ERASER_MODE }, 11348c2ecf20Sopenharmony_ci { "pencil", AIPTEK_TOOL_BUTTON_PENCIL_MODE }, 11358c2ecf20Sopenharmony_ci { "pen", AIPTEK_TOOL_BUTTON_PEN_MODE }, 11368c2ecf20Sopenharmony_ci { "brush", AIPTEK_TOOL_BUTTON_BRUSH_MODE }, 11378c2ecf20Sopenharmony_ci { "airbrush", AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE }, 11388c2ecf20Sopenharmony_ci { "lens", AIPTEK_TOOL_BUTTON_LENS_MODE }, 11398c2ecf20Sopenharmony_ci { NULL, AIPTEK_INVALID_VALUE } 11408c2ecf20Sopenharmony_ci}; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cistatic ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 11478c2ecf20Sopenharmony_ci map_val_to_str(tool_mode_map, 11488c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode)); 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic ssize_t 11528c2ecf20Sopenharmony_cistore_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 11558c2ecf20Sopenharmony_ci int new_mode = map_str_to_val(tool_mode_map, buf, count); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (new_mode == AIPTEK_INVALID_VALUE) 11588c2ecf20Sopenharmony_ci return -EINVAL; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci aiptek->newSetting.toolMode = new_mode; 11618c2ecf20Sopenharmony_ci return count; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tool_mode, 11658c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 11668c2ecf20Sopenharmony_ci show_tabletToolMode, store_tabletToolMode); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci/*********************************************************************** 11698c2ecf20Sopenharmony_ci * support routines for the 'xtilt' file. Note that this file 11708c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_cistatic ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) { 11778c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "disable\n"); 11788c2ecf20Sopenharmony_ci } else { 11798c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 11808c2ecf20Sopenharmony_ci aiptek->curSetting.xTilt); 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic ssize_t 11858c2ecf20Sopenharmony_cistore_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 11888c2ecf20Sopenharmony_ci int x; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, &x)) { 11918c2ecf20Sopenharmony_ci size_t len = buf[count - 1] == '\n' ? count - 1 : count; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (strncmp(buf, "disable", len)) 11948c2ecf20Sopenharmony_ci return -EINVAL; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE; 11978c2ecf20Sopenharmony_ci } else { 11988c2ecf20Sopenharmony_ci if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX) 11998c2ecf20Sopenharmony_ci return -EINVAL; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci aiptek->newSetting.xTilt = x; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci return count; 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic DEVICE_ATTR(xtilt, 12088c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, show_tabletXtilt, store_tabletXtilt); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci/*********************************************************************** 12118c2ecf20Sopenharmony_ci * support routines for the 'ytilt' file. Note that this file 12128c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_cistatic ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) { 12198c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "disable\n"); 12208c2ecf20Sopenharmony_ci } else { 12218c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 12228c2ecf20Sopenharmony_ci aiptek->curSetting.yTilt); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic ssize_t 12278c2ecf20Sopenharmony_cistore_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 12308c2ecf20Sopenharmony_ci int y; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, &y)) { 12338c2ecf20Sopenharmony_ci size_t len = buf[count - 1] == '\n' ? count - 1 : count; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (strncmp(buf, "disable", len)) 12368c2ecf20Sopenharmony_ci return -EINVAL; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE; 12398c2ecf20Sopenharmony_ci } else { 12408c2ecf20Sopenharmony_ci if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX) 12418c2ecf20Sopenharmony_ci return -EINVAL; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci aiptek->newSetting.yTilt = y; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return count; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ytilt, 12508c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, show_tabletYtilt, store_tabletYtilt); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci/*********************************************************************** 12538c2ecf20Sopenharmony_ci * support routines for the 'jitter' file. Note that this file 12548c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_cistatic ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic ssize_t 12648c2ecf20Sopenharmony_cistore_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 12678c2ecf20Sopenharmony_ci int err, j; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci err = kstrtoint(buf, 10, &j); 12708c2ecf20Sopenharmony_ci if (err) 12718c2ecf20Sopenharmony_ci return err; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci aiptek->newSetting.jitterDelay = j; 12748c2ecf20Sopenharmony_ci return count; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic DEVICE_ATTR(jitter, 12788c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 12798c2ecf20Sopenharmony_ci show_tabletJitterDelay, store_tabletJitterDelay); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci/*********************************************************************** 12828c2ecf20Sopenharmony_ci * support routines for the 'delay' file. Note that this file 12838c2ecf20Sopenharmony_ci * both displays current setting and allows reprogramming. 12848c2ecf20Sopenharmony_ci */ 12858c2ecf20Sopenharmony_cistatic ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 12908c2ecf20Sopenharmony_ci aiptek->curSetting.programmableDelay); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic ssize_t 12948c2ecf20Sopenharmony_cistore_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 12978c2ecf20Sopenharmony_ci int err, d; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci err = kstrtoint(buf, 10, &d); 13008c2ecf20Sopenharmony_ci if (err) 13018c2ecf20Sopenharmony_ci return err; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci aiptek->newSetting.programmableDelay = d; 13048c2ecf20Sopenharmony_ci return count; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic DEVICE_ATTR(delay, 13088c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 13098c2ecf20Sopenharmony_ci show_tabletProgrammableDelay, store_tabletProgrammableDelay); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci/*********************************************************************** 13128c2ecf20Sopenharmony_ci * support routines for the 'event_count' file. Note that this file 13138c2ecf20Sopenharmony_ci * only displays current setting. 13148c2ecf20Sopenharmony_ci */ 13158c2ecf20Sopenharmony_cistatic ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); 13208c2ecf20Sopenharmony_ci} 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci/*********************************************************************** 13258c2ecf20Sopenharmony_ci * support routines for the 'diagnostic' file. Note that this file 13268c2ecf20Sopenharmony_ci * only displays current setting. 13278c2ecf20Sopenharmony_ci */ 13288c2ecf20Sopenharmony_cistatic ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 13318c2ecf20Sopenharmony_ci char *retMsg; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci switch (aiptek->diagnostic) { 13348c2ecf20Sopenharmony_ci case AIPTEK_DIAGNOSTIC_NA: 13358c2ecf20Sopenharmony_ci retMsg = "no errors\n"; 13368c2ecf20Sopenharmony_ci break; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE: 13398c2ecf20Sopenharmony_ci retMsg = "Error: receiving relative reports\n"; 13408c2ecf20Sopenharmony_ci break; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE: 13438c2ecf20Sopenharmony_ci retMsg = "Error: receiving absolute reports\n"; 13448c2ecf20Sopenharmony_ci break; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED: 13478c2ecf20Sopenharmony_ci if (aiptek->curSetting.pointerMode == 13488c2ecf20Sopenharmony_ci AIPTEK_POINTER_ONLY_MOUSE_MODE) { 13498c2ecf20Sopenharmony_ci retMsg = "Error: receiving stylus reports\n"; 13508c2ecf20Sopenharmony_ci } else { 13518c2ecf20Sopenharmony_ci retMsg = "Error: receiving mouse reports\n"; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci break; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci default: 13568c2ecf20Sopenharmony_ci return 0; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, retMsg); 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci/*********************************************************************** 13648c2ecf20Sopenharmony_ci * support routines for the 'stylus_upper' file. Note that this file 13658c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 13668c2ecf20Sopenharmony_ci */ 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic struct aiptek_map stylus_button_map[] = { 13698c2ecf20Sopenharmony_ci { "upper", AIPTEK_STYLUS_UPPER_BUTTON }, 13708c2ecf20Sopenharmony_ci { "lower", AIPTEK_STYLUS_LOWER_BUTTON }, 13718c2ecf20Sopenharmony_ci { NULL, AIPTEK_INVALID_VALUE } 13728c2ecf20Sopenharmony_ci}; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_cistatic ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf) 13758c2ecf20Sopenharmony_ci{ 13768c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 13798c2ecf20Sopenharmony_ci map_val_to_str(stylus_button_map, 13808c2ecf20Sopenharmony_ci aiptek->curSetting.stylusButtonUpper)); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic ssize_t 13848c2ecf20Sopenharmony_cistore_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 13878c2ecf20Sopenharmony_ci int new_button = map_str_to_val(stylus_button_map, buf, count); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (new_button == AIPTEK_INVALID_VALUE) 13908c2ecf20Sopenharmony_ci return -EINVAL; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci aiptek->newSetting.stylusButtonUpper = new_button; 13938c2ecf20Sopenharmony_ci return count; 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic DEVICE_ATTR(stylus_upper, 13978c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 13988c2ecf20Sopenharmony_ci show_tabletStylusUpper, store_tabletStylusUpper); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci/*********************************************************************** 14018c2ecf20Sopenharmony_ci * support routines for the 'stylus_lower' file. Note that this file 14028c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 14108c2ecf20Sopenharmony_ci map_val_to_str(stylus_button_map, 14118c2ecf20Sopenharmony_ci aiptek->curSetting.stylusButtonLower)); 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cistatic ssize_t 14158c2ecf20Sopenharmony_cistore_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 14188c2ecf20Sopenharmony_ci int new_button = map_str_to_val(stylus_button_map, buf, count); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (new_button == AIPTEK_INVALID_VALUE) 14218c2ecf20Sopenharmony_ci return -EINVAL; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci aiptek->newSetting.stylusButtonLower = new_button; 14248c2ecf20Sopenharmony_ci return count; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic DEVICE_ATTR(stylus_lower, 14288c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 14298c2ecf20Sopenharmony_ci show_tabletStylusLower, store_tabletStylusLower); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci/*********************************************************************** 14328c2ecf20Sopenharmony_ci * support routines for the 'mouse_left' file. Note that this file 14338c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 14348c2ecf20Sopenharmony_ci */ 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic struct aiptek_map mouse_button_map[] = { 14378c2ecf20Sopenharmony_ci { "left", AIPTEK_MOUSE_LEFT_BUTTON }, 14388c2ecf20Sopenharmony_ci { "middle", AIPTEK_MOUSE_MIDDLE_BUTTON }, 14398c2ecf20Sopenharmony_ci { "right", AIPTEK_MOUSE_RIGHT_BUTTON }, 14408c2ecf20Sopenharmony_ci { NULL, AIPTEK_INVALID_VALUE } 14418c2ecf20Sopenharmony_ci}; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf) 14448c2ecf20Sopenharmony_ci{ 14458c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 14488c2ecf20Sopenharmony_ci map_val_to_str(mouse_button_map, 14498c2ecf20Sopenharmony_ci aiptek->curSetting.mouseButtonLeft)); 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic ssize_t 14538c2ecf20Sopenharmony_cistore_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 14568c2ecf20Sopenharmony_ci int new_button = map_str_to_val(mouse_button_map, buf, count); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (new_button == AIPTEK_INVALID_VALUE) 14598c2ecf20Sopenharmony_ci return -EINVAL; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci aiptek->newSetting.mouseButtonLeft = new_button; 14628c2ecf20Sopenharmony_ci return count; 14638c2ecf20Sopenharmony_ci} 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mouse_left, 14668c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 14678c2ecf20Sopenharmony_ci show_tabletMouseLeft, store_tabletMouseLeft); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci/*********************************************************************** 14708c2ecf20Sopenharmony_ci * support routines for the 'mouse_middle' file. Note that this file 14718c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 14728c2ecf20Sopenharmony_ci */ 14738c2ecf20Sopenharmony_cistatic ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 14788c2ecf20Sopenharmony_ci map_val_to_str(mouse_button_map, 14798c2ecf20Sopenharmony_ci aiptek->curSetting.mouseButtonMiddle)); 14808c2ecf20Sopenharmony_ci} 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_cistatic ssize_t 14838c2ecf20Sopenharmony_cistore_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 14868c2ecf20Sopenharmony_ci int new_button = map_str_to_val(mouse_button_map, buf, count); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (new_button == AIPTEK_INVALID_VALUE) 14898c2ecf20Sopenharmony_ci return -EINVAL; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci aiptek->newSetting.mouseButtonMiddle = new_button; 14928c2ecf20Sopenharmony_ci return count; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mouse_middle, 14968c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 14978c2ecf20Sopenharmony_ci show_tabletMouseMiddle, store_tabletMouseMiddle); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci/*********************************************************************** 15008c2ecf20Sopenharmony_ci * support routines for the 'mouse_right' file. Note that this file 15018c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 15028c2ecf20Sopenharmony_ci */ 15038c2ecf20Sopenharmony_cistatic ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf) 15048c2ecf20Sopenharmony_ci{ 15058c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 15088c2ecf20Sopenharmony_ci map_val_to_str(mouse_button_map, 15098c2ecf20Sopenharmony_ci aiptek->curSetting.mouseButtonRight)); 15108c2ecf20Sopenharmony_ci} 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_cistatic ssize_t 15138c2ecf20Sopenharmony_cistore_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15148c2ecf20Sopenharmony_ci{ 15158c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 15168c2ecf20Sopenharmony_ci int new_button = map_str_to_val(mouse_button_map, buf, count); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if (new_button == AIPTEK_INVALID_VALUE) 15198c2ecf20Sopenharmony_ci return -EINVAL; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci aiptek->newSetting.mouseButtonRight = new_button; 15228c2ecf20Sopenharmony_ci return count; 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mouse_right, 15268c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, 15278c2ecf20Sopenharmony_ci show_tabletMouseRight, store_tabletMouseRight); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci/*********************************************************************** 15308c2ecf20Sopenharmony_ci * support routines for the 'wheel' file. Note that this file 15318c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 15328c2ecf20Sopenharmony_ci */ 15338c2ecf20Sopenharmony_cistatic ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) { 15388c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "disable\n"); 15398c2ecf20Sopenharmony_ci } else { 15408c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 15418c2ecf20Sopenharmony_ci aiptek->curSetting.wheel); 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci} 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_cistatic ssize_t 15468c2ecf20Sopenharmony_cistore_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15478c2ecf20Sopenharmony_ci{ 15488c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 15498c2ecf20Sopenharmony_ci int err, w; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci err = kstrtoint(buf, 10, &w); 15528c2ecf20Sopenharmony_ci if (err) 15538c2ecf20Sopenharmony_ci return err; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci aiptek->newSetting.wheel = w; 15568c2ecf20Sopenharmony_ci return count; 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(wheel, 15608c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, show_tabletWheel, store_tabletWheel); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci/*********************************************************************** 15638c2ecf20Sopenharmony_ci * support routines for the 'execute' file. Note that this file 15648c2ecf20Sopenharmony_ci * both displays current setting and allows for setting changing. 15658c2ecf20Sopenharmony_ci */ 15668c2ecf20Sopenharmony_cistatic ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf) 15678c2ecf20Sopenharmony_ci{ 15688c2ecf20Sopenharmony_ci /* There is nothing useful to display, so a one-line manual 15698c2ecf20Sopenharmony_ci * is in order... 15708c2ecf20Sopenharmony_ci */ 15718c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, 15728c2ecf20Sopenharmony_ci "Write anything to this file to program your tablet.\n"); 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cistatic ssize_t 15768c2ecf20Sopenharmony_cistore_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci /* We do not care what you write to this file. Merely the action 15818c2ecf20Sopenharmony_ci * of writing to this file triggers a tablet reprogramming. 15828c2ecf20Sopenharmony_ci */ 15838c2ecf20Sopenharmony_ci memcpy(&aiptek->curSetting, &aiptek->newSetting, 15848c2ecf20Sopenharmony_ci sizeof(struct aiptek_settings)); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (aiptek_program_tablet(aiptek) < 0) 15878c2ecf20Sopenharmony_ci return -EIO; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci return count; 15908c2ecf20Sopenharmony_ci} 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_cistatic DEVICE_ATTR(execute, 15938c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, show_tabletExecute, store_tabletExecute); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci/*********************************************************************** 15968c2ecf20Sopenharmony_ci * support routines for the 'odm_code' file. Note that this file 15978c2ecf20Sopenharmony_ci * only displays current setting. 15988c2ecf20Sopenharmony_ci */ 15998c2ecf20Sopenharmony_cistatic ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cistatic DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci/*********************************************************************** 16098c2ecf20Sopenharmony_ci * support routines for the 'model_code' file. Note that this file 16108c2ecf20Sopenharmony_ci * only displays current setting. 16118c2ecf20Sopenharmony_ci */ 16128c2ecf20Sopenharmony_cistatic ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci/*********************************************************************** 16228c2ecf20Sopenharmony_ci * support routines for the 'firmware_code' file. Note that this file 16238c2ecf20Sopenharmony_ci * only displays current setting. 16248c2ecf20Sopenharmony_ci */ 16258c2ecf20Sopenharmony_cistatic ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci struct aiptek *aiptek = dev_get_drvdata(dev); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%04x\n", 16308c2ecf20Sopenharmony_ci aiptek->features.firmwareCode); 16318c2ecf20Sopenharmony_ci} 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_cistatic DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_cistatic struct attribute *aiptek_attributes[] = { 16368c2ecf20Sopenharmony_ci &dev_attr_size.attr, 16378c2ecf20Sopenharmony_ci &dev_attr_pointer_mode.attr, 16388c2ecf20Sopenharmony_ci &dev_attr_coordinate_mode.attr, 16398c2ecf20Sopenharmony_ci &dev_attr_tool_mode.attr, 16408c2ecf20Sopenharmony_ci &dev_attr_xtilt.attr, 16418c2ecf20Sopenharmony_ci &dev_attr_ytilt.attr, 16428c2ecf20Sopenharmony_ci &dev_attr_jitter.attr, 16438c2ecf20Sopenharmony_ci &dev_attr_delay.attr, 16448c2ecf20Sopenharmony_ci &dev_attr_event_count.attr, 16458c2ecf20Sopenharmony_ci &dev_attr_diagnostic.attr, 16468c2ecf20Sopenharmony_ci &dev_attr_odm_code.attr, 16478c2ecf20Sopenharmony_ci &dev_attr_model_code.attr, 16488c2ecf20Sopenharmony_ci &dev_attr_firmware_code.attr, 16498c2ecf20Sopenharmony_ci &dev_attr_stylus_lower.attr, 16508c2ecf20Sopenharmony_ci &dev_attr_stylus_upper.attr, 16518c2ecf20Sopenharmony_ci &dev_attr_mouse_left.attr, 16528c2ecf20Sopenharmony_ci &dev_attr_mouse_middle.attr, 16538c2ecf20Sopenharmony_ci &dev_attr_mouse_right.attr, 16548c2ecf20Sopenharmony_ci &dev_attr_wheel.attr, 16558c2ecf20Sopenharmony_ci &dev_attr_execute.attr, 16568c2ecf20Sopenharmony_ci NULL 16578c2ecf20Sopenharmony_ci}; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic const struct attribute_group aiptek_attribute_group = { 16608c2ecf20Sopenharmony_ci .attrs = aiptek_attributes, 16618c2ecf20Sopenharmony_ci}; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci/*********************************************************************** 16648c2ecf20Sopenharmony_ci * This routine is called when a tablet has been identified. It basically 16658c2ecf20Sopenharmony_ci * sets up the tablet and the driver's internal structures. 16668c2ecf20Sopenharmony_ci */ 16678c2ecf20Sopenharmony_cistatic int 16688c2ecf20Sopenharmony_ciaiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) 16698c2ecf20Sopenharmony_ci{ 16708c2ecf20Sopenharmony_ci struct usb_device *usbdev = interface_to_usbdev(intf); 16718c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 16728c2ecf20Sopenharmony_ci struct aiptek *aiptek; 16738c2ecf20Sopenharmony_ci struct input_dev *inputdev; 16748c2ecf20Sopenharmony_ci int i; 16758c2ecf20Sopenharmony_ci int speeds[] = { 0, 16768c2ecf20Sopenharmony_ci AIPTEK_PROGRAMMABLE_DELAY_50, 16778c2ecf20Sopenharmony_ci AIPTEK_PROGRAMMABLE_DELAY_400, 16788c2ecf20Sopenharmony_ci AIPTEK_PROGRAMMABLE_DELAY_25, 16798c2ecf20Sopenharmony_ci AIPTEK_PROGRAMMABLE_DELAY_100, 16808c2ecf20Sopenharmony_ci AIPTEK_PROGRAMMABLE_DELAY_200, 16818c2ecf20Sopenharmony_ci AIPTEK_PROGRAMMABLE_DELAY_300 16828c2ecf20Sopenharmony_ci }; 16838c2ecf20Sopenharmony_ci int err = -ENOMEM; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci /* programmableDelay is where the command-line specified 16868c2ecf20Sopenharmony_ci * delay is kept. We make it the first element of speeds[], 16878c2ecf20Sopenharmony_ci * so therefore, your override speed is tried first, then the 16888c2ecf20Sopenharmony_ci * remainder. Note that the default value of 400ms will be tried 16898c2ecf20Sopenharmony_ci * if you do not specify any command line parameter. 16908c2ecf20Sopenharmony_ci */ 16918c2ecf20Sopenharmony_ci speeds[0] = programmableDelay; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL); 16948c2ecf20Sopenharmony_ci inputdev = input_allocate_device(); 16958c2ecf20Sopenharmony_ci if (!aiptek || !inputdev) { 16968c2ecf20Sopenharmony_ci dev_warn(&intf->dev, 16978c2ecf20Sopenharmony_ci "cannot allocate memory or input device\n"); 16988c2ecf20Sopenharmony_ci goto fail1; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci aiptek->data = usb_alloc_coherent(usbdev, AIPTEK_PACKET_LENGTH, 17028c2ecf20Sopenharmony_ci GFP_KERNEL, &aiptek->data_dma); 17038c2ecf20Sopenharmony_ci if (!aiptek->data) { 17048c2ecf20Sopenharmony_ci dev_warn(&intf->dev, "cannot allocate usb buffer\n"); 17058c2ecf20Sopenharmony_ci goto fail1; 17068c2ecf20Sopenharmony_ci } 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci aiptek->urb = usb_alloc_urb(0, GFP_KERNEL); 17098c2ecf20Sopenharmony_ci if (!aiptek->urb) { 17108c2ecf20Sopenharmony_ci dev_warn(&intf->dev, "cannot allocate urb\n"); 17118c2ecf20Sopenharmony_ci goto fail2; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci aiptek->inputdev = inputdev; 17158c2ecf20Sopenharmony_ci aiptek->intf = intf; 17168c2ecf20Sopenharmony_ci aiptek->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; 17178c2ecf20Sopenharmony_ci aiptek->inDelay = 0; 17188c2ecf20Sopenharmony_ci aiptek->endDelay = 0; 17198c2ecf20Sopenharmony_ci aiptek->previousJitterable = 0; 17208c2ecf20Sopenharmony_ci aiptek->lastMacro = -1; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci /* Set up the curSettings struct. Said struct contains the current 17238c2ecf20Sopenharmony_ci * programmable parameters. The newSetting struct contains changes 17248c2ecf20Sopenharmony_ci * the user makes to the settings via the sysfs interface. Those 17258c2ecf20Sopenharmony_ci * changes are not "committed" to curSettings until the user 17268c2ecf20Sopenharmony_ci * writes to the sysfs/.../execute file. 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_ci aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE; 17298c2ecf20Sopenharmony_ci aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE; 17308c2ecf20Sopenharmony_ci aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE; 17318c2ecf20Sopenharmony_ci aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE; 17328c2ecf20Sopenharmony_ci aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE; 17338c2ecf20Sopenharmony_ci aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON; 17348c2ecf20Sopenharmony_ci aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON; 17358c2ecf20Sopenharmony_ci aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON; 17368c2ecf20Sopenharmony_ci aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON; 17378c2ecf20Sopenharmony_ci aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON; 17388c2ecf20Sopenharmony_ci aiptek->curSetting.jitterDelay = jitterDelay; 17398c2ecf20Sopenharmony_ci aiptek->curSetting.programmableDelay = programmableDelay; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci /* Both structs should have equivalent settings 17428c2ecf20Sopenharmony_ci */ 17438c2ecf20Sopenharmony_ci aiptek->newSetting = aiptek->curSetting; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci /* Determine the usb devices' physical path. 17468c2ecf20Sopenharmony_ci * Asketh not why we always pretend we're using "../input0", 17478c2ecf20Sopenharmony_ci * but I suspect this will have to be refactored one 17488c2ecf20Sopenharmony_ci * day if a single USB device can be a keyboard & a mouse 17498c2ecf20Sopenharmony_ci * & a tablet, and the inputX number actually will tell 17508c2ecf20Sopenharmony_ci * us something... 17518c2ecf20Sopenharmony_ci */ 17528c2ecf20Sopenharmony_ci usb_make_path(usbdev, aiptek->features.usbPath, 17538c2ecf20Sopenharmony_ci sizeof(aiptek->features.usbPath)); 17548c2ecf20Sopenharmony_ci strlcat(aiptek->features.usbPath, "/input0", 17558c2ecf20Sopenharmony_ci sizeof(aiptek->features.usbPath)); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* Set up client data, pointers to open and close routines 17588c2ecf20Sopenharmony_ci * for the input device. 17598c2ecf20Sopenharmony_ci */ 17608c2ecf20Sopenharmony_ci inputdev->name = "Aiptek"; 17618c2ecf20Sopenharmony_ci inputdev->phys = aiptek->features.usbPath; 17628c2ecf20Sopenharmony_ci usb_to_input_id(usbdev, &inputdev->id); 17638c2ecf20Sopenharmony_ci inputdev->dev.parent = &intf->dev; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci input_set_drvdata(inputdev, aiptek); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci inputdev->open = aiptek_open; 17688c2ecf20Sopenharmony_ci inputdev->close = aiptek_close; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci /* Now program the capacities of the tablet, in terms of being 17718c2ecf20Sopenharmony_ci * an input device. 17728c2ecf20Sopenharmony_ci */ 17738c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(eventTypes); ++i) 17748c2ecf20Sopenharmony_ci __set_bit(eventTypes[i], inputdev->evbit); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(absEvents); ++i) 17778c2ecf20Sopenharmony_ci __set_bit(absEvents[i], inputdev->absbit); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(relEvents); ++i) 17808c2ecf20Sopenharmony_ci __set_bit(relEvents[i], inputdev->relbit); 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci __set_bit(MSC_SERIAL, inputdev->mscbit); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci /* Set up key and button codes */ 17858c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i) 17868c2ecf20Sopenharmony_ci __set_bit(buttonEvents[i], inputdev->keybit); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i) 17898c2ecf20Sopenharmony_ci __set_bit(macroKeyEvents[i], inputdev->keybit); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci /* 17928c2ecf20Sopenharmony_ci * Program the input device coordinate capacities. We do not yet 17938c2ecf20Sopenharmony_ci * know what maximum X, Y, and Z values are, so we're putting fake 17948c2ecf20Sopenharmony_ci * values in. Later, we'll ask the tablet to put in the correct 17958c2ecf20Sopenharmony_ci * values. 17968c2ecf20Sopenharmony_ci */ 17978c2ecf20Sopenharmony_ci input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0); 17988c2ecf20Sopenharmony_ci input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0); 17998c2ecf20Sopenharmony_ci input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0); 18008c2ecf20Sopenharmony_ci input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); 18018c2ecf20Sopenharmony_ci input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); 18028c2ecf20Sopenharmony_ci input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci err = usb_find_common_endpoints(intf->cur_altsetting, 18058c2ecf20Sopenharmony_ci NULL, NULL, &endpoint, NULL); 18068c2ecf20Sopenharmony_ci if (err) { 18078c2ecf20Sopenharmony_ci dev_err(&intf->dev, 18088c2ecf20Sopenharmony_ci "interface has no int in endpoints, but must have minimum 1\n"); 18098c2ecf20Sopenharmony_ci goto fail3; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* Go set up our URB, which is called when the tablet receives 18138c2ecf20Sopenharmony_ci * input. 18148c2ecf20Sopenharmony_ci */ 18158c2ecf20Sopenharmony_ci usb_fill_int_urb(aiptek->urb, 18168c2ecf20Sopenharmony_ci usbdev, 18178c2ecf20Sopenharmony_ci usb_rcvintpipe(usbdev, 18188c2ecf20Sopenharmony_ci endpoint->bEndpointAddress), 18198c2ecf20Sopenharmony_ci aiptek->data, 8, aiptek_irq, aiptek, 18208c2ecf20Sopenharmony_ci endpoint->bInterval); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci aiptek->urb->transfer_dma = aiptek->data_dma; 18238c2ecf20Sopenharmony_ci aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci /* Program the tablet. This sets the tablet up in the mode 18268c2ecf20Sopenharmony_ci * specified in newSetting, and also queries the tablet's 18278c2ecf20Sopenharmony_ci * physical capacities. 18288c2ecf20Sopenharmony_ci * 18298c2ecf20Sopenharmony_ci * Sanity check: if a tablet doesn't like the slow programmatic 18308c2ecf20Sopenharmony_ci * delay, we often get sizes of 0x0. Let's use that as an indicator 18318c2ecf20Sopenharmony_ci * to try faster delays, up to 25 ms. If that logic fails, well, you'll 18328c2ecf20Sopenharmony_ci * have to explain to us how your tablet thinks it's 0x0, and yet that's 18338c2ecf20Sopenharmony_ci * not an error :-) 18348c2ecf20Sopenharmony_ci */ 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(speeds); ++i) { 18378c2ecf20Sopenharmony_ci aiptek->curSetting.programmableDelay = speeds[i]; 18388c2ecf20Sopenharmony_ci (void)aiptek_program_tablet(aiptek); 18398c2ecf20Sopenharmony_ci if (input_abs_get_max(aiptek->inputdev, ABS_X) > 0) { 18408c2ecf20Sopenharmony_ci dev_info(&intf->dev, 18418c2ecf20Sopenharmony_ci "Aiptek using %d ms programming speed\n", 18428c2ecf20Sopenharmony_ci aiptek->curSetting.programmableDelay); 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci /* Murphy says that some day someone will have a tablet that fails the 18488c2ecf20Sopenharmony_ci above test. That's you, Frederic Rodrigo */ 18498c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(speeds)) { 18508c2ecf20Sopenharmony_ci dev_info(&intf->dev, 18518c2ecf20Sopenharmony_ci "Aiptek tried all speeds, no sane response\n"); 18528c2ecf20Sopenharmony_ci err = -EINVAL; 18538c2ecf20Sopenharmony_ci goto fail3; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci /* Associate this driver's struct with the usb interface. 18578c2ecf20Sopenharmony_ci */ 18588c2ecf20Sopenharmony_ci usb_set_intfdata(intf, aiptek); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci /* Set up the sysfs files 18618c2ecf20Sopenharmony_ci */ 18628c2ecf20Sopenharmony_ci err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group); 18638c2ecf20Sopenharmony_ci if (err) { 18648c2ecf20Sopenharmony_ci dev_warn(&intf->dev, "cannot create sysfs group err: %d\n", 18658c2ecf20Sopenharmony_ci err); 18668c2ecf20Sopenharmony_ci goto fail3; 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* Register the tablet as an Input Device 18708c2ecf20Sopenharmony_ci */ 18718c2ecf20Sopenharmony_ci err = input_register_device(aiptek->inputdev); 18728c2ecf20Sopenharmony_ci if (err) { 18738c2ecf20Sopenharmony_ci dev_warn(&intf->dev, 18748c2ecf20Sopenharmony_ci "input_register_device returned err: %d\n", err); 18758c2ecf20Sopenharmony_ci goto fail4; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci return 0; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci fail4: sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); 18808c2ecf20Sopenharmony_ci fail3: usb_free_urb(aiptek->urb); 18818c2ecf20Sopenharmony_ci fail2: usb_free_coherent(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data, 18828c2ecf20Sopenharmony_ci aiptek->data_dma); 18838c2ecf20Sopenharmony_ci fail1: usb_set_intfdata(intf, NULL); 18848c2ecf20Sopenharmony_ci input_free_device(inputdev); 18858c2ecf20Sopenharmony_ci kfree(aiptek); 18868c2ecf20Sopenharmony_ci return err; 18878c2ecf20Sopenharmony_ci} 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci/*********************************************************************** 18908c2ecf20Sopenharmony_ci * Deal with tablet disconnecting from the system. 18918c2ecf20Sopenharmony_ci */ 18928c2ecf20Sopenharmony_cistatic void aiptek_disconnect(struct usb_interface *intf) 18938c2ecf20Sopenharmony_ci{ 18948c2ecf20Sopenharmony_ci struct aiptek *aiptek = usb_get_intfdata(intf); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci /* Disassociate driver's struct with usb interface 18978c2ecf20Sopenharmony_ci */ 18988c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 18998c2ecf20Sopenharmony_ci if (aiptek != NULL) { 19008c2ecf20Sopenharmony_ci /* Free & unhook everything from the system. 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_ci usb_kill_urb(aiptek->urb); 19038c2ecf20Sopenharmony_ci input_unregister_device(aiptek->inputdev); 19048c2ecf20Sopenharmony_ci sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); 19058c2ecf20Sopenharmony_ci usb_free_urb(aiptek->urb); 19068c2ecf20Sopenharmony_ci usb_free_coherent(interface_to_usbdev(intf), 19078c2ecf20Sopenharmony_ci AIPTEK_PACKET_LENGTH, 19088c2ecf20Sopenharmony_ci aiptek->data, aiptek->data_dma); 19098c2ecf20Sopenharmony_ci kfree(aiptek); 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci} 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cistatic struct usb_driver aiptek_driver = { 19148c2ecf20Sopenharmony_ci .name = "aiptek", 19158c2ecf20Sopenharmony_ci .probe = aiptek_probe, 19168c2ecf20Sopenharmony_ci .disconnect = aiptek_disconnect, 19178c2ecf20Sopenharmony_ci .id_table = aiptek_ids, 19188c2ecf20Sopenharmony_ci}; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_cimodule_usb_driver(aiptek_driver); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen"); 19238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Aiptek HyperPen USB Tablet Driver"); 19248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_cimodule_param(programmableDelay, int, 0); 19278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(programmableDelay, "delay used during tablet programming"); 19288c2ecf20Sopenharmony_cimodule_param(jitterDelay, int, 0); 19298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay"); 1930