// SPDX-License-Identifier: GPL-2.0-or-later
/*
- Native support for the Aiptek HyperPen USB Tablets
- (4000U/5000U/6000U/8000U/12000U)
- Many thanks to Oliver Kuechemann for his support.
- ChangeLog:
v0.1 - Initial release
v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
Released to Linux 2.4.19 and 2.5.x
v0.4 - Rewrote substantial portions of the code to deal with
corrected control sequences, timing, dynamic configuration,
support of 6000U - 12000U, procfs, and macro key support
(Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
v1.0 - Added support for diagnostic messages, count of messages
received from URB - Mar-8-2003, Bryan W. Headley
v1.1 - added support for tablet resolution, changed DV and proximity
some corrections - Jun-22-2003, martin schneebacher
- Added support for the sysfs interface, deprecating the
procfs interface for 2.5.x kernel. Also added support for
Wheel command. Bryan W. Headley July-15-2003.
v1.2 - Reworked jitter timer as a kernel thread.
Bryan W. Headley November-28-2003/Jan-10-2004.
v1.3 - Repaired issue of kernel thread going nuts on single-processor
machines, introduced programmableDelay as a command line
parameter. Feb 7 2004, Bryan W. Headley.
v1.4 - Re-wire jitter so it does not require a thread. Courtesy of
Rene van Paassen. Added reporting of physical pointer device
(e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know
for reports 1, 6.)
what physical device reports for reports 1, 6.) Also enabled
MOUSE and LENS tool button modes. Renamed "rubber" to "eraser".
Feb 20, 2004, Bryan W. Headley.
v1.5 - Added previousJitterable, so we don't do jitter delay when the
user is holding a button down for periods of time.
- NOTE:
This kernel driver is augmented by the "Aiptek" XFree86 input
driver for your X server, as well as the Gaiptek GUI Front-end
"Tablet Manager".
These three products are highly interactive with one another,
so therefore it's easier to document them all as one subsystem.
Please visit the project's "home page", located at,
http://aiptektablet.sourceforge.net.
*/
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
/*
- Aiptek status packet:
- (returned as Report 1 - relative coordinates from mouse and stylus)
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- byte0 0 0 0 0 0 0 0 1
- byte1 0 0 0 0 0 BS2 BS Tip
- byte2 X7 X6 X5 X4 X3 X2 X1 X0
- byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
- (returned as Report 2 - absolute coordinates from the stylus)
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- byte0 0 0 0 0 0 0 1 0
- byte1 X7 X6 X5 X4 X3 X2 X1 X0
- byte2 X15 X14 X13 X12 X11 X10 X9 X8
- byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
- byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8
- byte5 * * * BS2 BS1 Tip IR DV
- byte6 P7 P6 P5 P4 P3 P2 P1 P0
- byte7 P15 P14 P13 P12 P11 P10 P9 P8
- (returned as Report 3 - absolute coordinates from the mouse)
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- byte0 0 0 0 0 0 0 1 1
- byte1 X7 X6 X5 X4 X3 X2 X1 X0
- byte2 X15 X14 X13 X12 X11 X10 X9 X8
- byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
- byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8
- byte5 * * * BS2 BS1 Tip IR DV
- byte6 P7 P6 P5 P4 P3 P2 P1 P0
- byte7 P15 P14 P13 P12 P11 P10 P9 P8
- (returned as Report 4 - macrokeys from the stylus)
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- byte0 0 0 0 0 0 1 0 0
- byte1 0 0 0 BS2 BS Tip IR DV
- byte2 0 0 0 0 0 0 1 0
- byte3 0 0 0 K4 K3 K2 K1 K0
- byte4 P7 P6 P5 P4 P3 P2 P1 P0
- byte5 P15 P14 P13 P12 P11 P10 P9 P8
- (returned as Report 5 - macrokeys from the mouse)
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- byte0 0 0 0 0 0 1 0 1
- byte1 0 0 0 BS2 BS Tip IR DV
- byte2 0 0 0 0 0 0 1 0
- byte3 0 0 0 K4 K3 K2 K1 K0
- byte4 P7 P6 P5 P4 P3 P2 P1 P0
- byte5 P15 P14 P13 P12 P11 P10 P9 P8
- IR: In Range = Proximity on
- DV = Data Valid
- BS = Barrel Switch (as in, macro keys)
- BS2 also referred to as Tablet Pick
- Command Summary:
- Use report_type CONTROL (3)
- Use report_id 2
- Command/Data Description Return Bytes Return Value
- 0x10/0x00 SwitchToMouse 0
- 0x10/0x01 SwitchToTablet 0
- 0x18/0x04 SetResolution 0
- 0x12/0xFF AutoGainOn 0
- 0x17/0x00 FilterOn 0
- 0x01/0x00 GetXExtension 2 MaxX
- 0x01/0x01 GetYExtension 2 MaxY
- 0x02/0x00 GetModelCode 2 ModelCode = LOBYTE
- 0x03/0x00 GetODMCode 2 ODMCode
- 0x08/0x00 GetPressureLevels 2 =512
- 0x04/0x00 GetFirmwareVersion 2 Firmware Version
- 0x11/0x02 EnableMacroKeys 0
- To initialize the tablet:
- (1) Send Resolution500LPI (Command)
- (2) Query for Model code (Option Report)
- (3) Query for ODM code (Option Report)
- (4) Query for firmware (Option Report)
- (5) Query for GetXExtension (Option Report)
- (6) Query for GetYExtension (Option Report)
- (7) Query for GetPressureLevels (Option Report)
- (8) SwitchToTablet for Absolute coordinates, or
SwitchToMouse for Relative coordinates (Command)
- (9) EnableMacroKeys (Command)
- (10) FilterOn (Command)
- (11) AutoGainOn (Command)
- (Step 9 can be omitted, but you’ll then have no function keys.)
*/
#define USB_VENDOR_ID_AIPTEK 0x08ca
#define USB_VENDOR_ID_KYE 0x0458
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_SET_REPORT 0x09
/* PointerMode codes
*/
#define AIPTEK_POINTER_ONLY_MOUSE_MODE 0
#define AIPTEK_POINTER_ONLY_STYLUS_MODE 1
#define AIPTEK_POINTER_EITHER_MODE 2
#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a)
(a == AIPTEK_POINTER_ONLY_MOUSE_MODE ||
a == AIPTEK_POINTER_EITHER_MODE)
#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a)
(a == AIPTEK_POINTER_ONLY_STYLUS_MODE ||
a == AIPTEK_POINTER_EITHER_MODE)
/* CoordinateMode code
*/
#define AIPTEK_COORDINATE_RELATIVE_MODE 0
#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1
/* XTilt and YTilt values
*/
#define AIPTEK_TILT_MIN (-128)
#define AIPTEK_TILT_MAX 127
#define AIPTEK_TILT_DISABLE (-10101)
/* Wheel values
*/
#define AIPTEK_WHEEL_MIN 0
#define AIPTEK_WHEEL_MAX 1024
#define AIPTEK_WHEEL_DISABLE (-10101)
/* ToolCode values, which BTW are 0x140 .. 0x14f
* We have things set up such that if the tool button has changed,
* the tools get reset.
*/
/* toolMode codes
*/
#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
#define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL
#define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH
#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH
#define AIPTEK_TOOL_BUTTON_ERASER_MODE BTN_TOOL_RUBBER
#define AIPTEK_TOOL_BUTTON_MOUSE_MODE BTN_TOOL_MOUSE
#define AIPTEK_TOOL_BUTTON_LENS_MODE BTN_TOOL_LENS
/* Diagnostic message codes
*/
#define AIPTEK_DIAGNOSTIC_NA 0
#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1
#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2
#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3
/* Time to wait (in ms) to help mask hand jittering
* when pressing the stylus buttons.
*/
#define AIPTEK_JITTER_DELAY_DEFAULT 50
/* Time to wait (in ms) in-between sending the tablet
* a command and beginning the process of reading the return
* sequence from the tablet.
*/
#define AIPTEK_PROGRAMMABLE_DELAY_25 25
#define AIPTEK_PROGRAMMABLE_DELAY_50 50
#define AIPTEK_PROGRAMMABLE_DELAY_100 100
#define AIPTEK_PROGRAMMABLE_DELAY_200 200
#define AIPTEK_PROGRAMMABLE_DELAY_300 300
#define AIPTEK_PROGRAMMABLE_DELAY_400 400
#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT AIPTEK_PROGRAMMABLE_DELAY_400
/* Mouse button programming
*/
#define AIPTEK_MOUSE_LEFT_BUTTON 0x04
#define AIPTEK_MOUSE_RIGHT_BUTTON 0x08
#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10
/* Stylus button programming
*/
#define AIPTEK_STYLUS_LOWER_BUTTON 0x08
#define AIPTEK_STYLUS_UPPER_BUTTON 0x10
/* Length of incoming packet from the tablet
*/
#define AIPTEK_PACKET_LENGTH 8
/* We report in EV_MISC both the proximity and
* whether the report came from the stylus, tablet mouse
* or "unknown" -- Unknown when the tablet is in relative
* mode, because we only get report 1's.
*/
#define AIPTEK_REPORT_TOOL_UNKNOWN 0x10
#define AIPTEK_REPORT_TOOL_STYLUS 0x20
#define AIPTEK_REPORT_TOOL_MOUSE 0x40
static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
struct aiptek_features {
int odmCode; /* Tablet manufacturer code /
int modelCode; / Tablet model code (not unique) /
int firmwareCode; / prom/eeprom version /
char usbPath[64 + 1]; / device’s physical usb path */
};
struct aiptek_settings {
int pointerMode; /* stylus-, mouse-only or either /
int coordinateMode; / absolute/relative coords /
int toolMode; / pen, pencil, brush, etc. tool /
int xTilt; / synthetic xTilt amount /
int yTilt; / synthetic yTilt amount /
int wheel; / synthetic wheel amount /
int stylusButtonUpper; / stylus upper btn delivers… /
int stylusButtonLower; / stylus lower btn delivers… /
int mouseButtonLeft; / mouse left btn delivers… /
int mouseButtonMiddle; / mouse middle btn delivers… /
int mouseButtonRight; / mouse right btn delivers… /
int programmableDelay; / delay for tablet programming /
int jitterDelay; / delay for hand jittering */
};
struct aiptek {
struct input_dev inputdev; / input device struct */
struct usb_interface intf; / usb interface struct */
struct urb urb; / urb for incoming reports /
dma_addr_t data_dma; / our dma stuffage /
struct aiptek_features features; / tablet’s array of features /
struct aiptek_settings curSetting; / tablet’s current programmable /
struct aiptek_settings newSetting; / … and new param settings /
unsigned int ifnum; / interface number for IO /
int diagnostic; / tablet diagnostic codes /
unsigned long eventCount; / event count /
int inDelay; / jitter: in jitter delay? /
unsigned long endDelay; / jitter: time when delay ends /
int previousJitterable; / jitterable prev value */
int lastMacro; /* macro key to reset */
int previousToolMode; /* pen, pencil, brush, etc. tool */
unsigned char *data; /* incoming packet data */
};
static const int eventTypes[] = {
EV_KEY, EV_ABS, EV_REL, EV_MSC,
};
static const int absEvents[] = {
ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y,
ABS_WHEEL, ABS_MISC,
};
static const int relEvents[] = {
REL_X, REL_Y, REL_WHEEL,
};
static const int buttonEvents[] = {
BTN_LEFT, BTN_RIGHT, BTN_MIDDLE,
BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH,
BTN_STYLUS, BTN_STYLUS2,
};
/*
- Permit easy lookup of keyboard events to send, versus
- the bitmap which comes from the tablet. This hides the
- issue that the F_keys are not sequentially numbered.
*/
static const int macroKeyEvents[] = {
KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
KEY_F24, K