Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
6aecb90d
Commit
6aecb90d
authored
May 17, 2004
by
Bryan W. Headley
Committed by
Greg Kroah-Hartman
May 17, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] USB: Aiptek.c Driver patch
parent
cc0b73f6
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
2300 additions
and
226 deletions
+2300
-226
drivers/usb/input/aiptek.c
drivers/usb/input/aiptek.c
+2286
-224
drivers/usb/input/hid-core.c
drivers/usb/input/hid-core.c
+14
-2
No files found.
drivers/usb/input/aiptek.c
View file @
6aecb90d
/*
* Native support for the Aiptek 8000U
* Native support for the Aiptek HyperPen USB Tablets
* (4000U/5000U/6000U/8000U/12000U)
*
* Copyright (c) 2001 Chris Atenasio <chris@crud.net>
* Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
*
* based on wacom.c by
* Vojtech Pavlik <vojtech@suse.cz>
...
...
@@ -11,17 +13,48 @@
* James E. Blair <corvus@gnu.org>
* Daniel Egger <egger@suse.de>
*
*
* Many thanks to Oliver Kuechemann for his support.
*
* ChangeLog:
* v0.1 - Initial release
* v0.2 - Hack to get around fake event 28's.
* v0.2 - Hack to get around fake event 28's.
(Bryan W. Headley)
* v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
* (kernel 2.5.x variant, June-14-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.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
...
...
@@ -37,373 +70,2402 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v0.3"
#define DRIVER_AUTHOR "Chris Atenasio <chris@crud.net>"
#define DRIVER_DESC "USB Aiptek 6000U/8000U tablet driver (Linux 2.5.x)"
MODULE_AUTHOR
(
DRIVER_AUTHOR
);
MODULE_DESCRIPTION
(
DRIVER_DESC
);
MODULE_LICENSE
(
"GPL"
);
#define DRIVER_VERSION "v1.5 (May-15-2004)"
#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio"
#define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
/*
* 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
DV IR
* 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 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 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 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
*
* 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 Resolution500LPI 0
* 0x17/0x00 FilterOn 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 command Resolution500LPI
* (2) Option Commands (GetXExtension, GetYExtension)
* (3) Send command SwitchToTablet
* (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_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 TOOL_BUTTON_FIRED_BIT is
* not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx.
*
* Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will
* get reset.
*/
#define TOOL_BUTTON(x) ((x) & 0x14f)
#define TOOL_BUTTON_FIRED(x) ((x) & 0x200)
#define TOOL_BUTTON_FIRED_BIT 0x200
/* toolMode codes
*/
#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
#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 0x01
#define AIPTEK_MOUSE_RIGHT_BUTTON 0x02
#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x04
/* 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
{
char
*
name
;
int
pktlen
;
int
x_max
;
int
y_max
;
int
pressure_min
;
int
pressure_max
;
usb_complete_t
irq
;
unsigned
long
evbit
;
unsigned
long
absbit
;
unsigned
long
relbit
;
unsigned
long
btnbit
;
unsigned
long
digibit
;
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 */
char
inputPath
[
64
+
1
];
/* input device path */
char
manuName
[
64
+
1
];
/* manufacturer name */
char
prodName
[
64
+
1
];
/* product name */
};
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
dev
;
struct
usb_device
*
usbdev
;
struct
urb
*
irq
;
struct
aiptek_features
*
features
;
int
tool
;
int
open
;
signed
char
*
data
;
dma_addr_t
data_dma
;
struct
input_dev
inputdev
;
/* input device struct */
struct
usb_device
*
usbdev
;
/* usb device 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
openCount
;
/* module use counter */
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 */
unsigned
char
*
data
;
/* incoming packet data */
};
/*
* 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
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
,
KEY_STOP
,
KEY_AGAIN
,
KEY_PROPS
,
KEY_UNDO
,
KEY_FRONT
,
KEY_COPY
,
KEY_OPEN
,
KEY_PASTE
,
0
};
static
void
aiptek_irq
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
)
/***********************************************************************
* Relative reports deliver values in 2's complement format to
* deal with negative offsets.
*/
static
int
aiptek_convert_from_2s_complement
(
unsigned
char
c
)
{
int
ret
;
unsigned
char
b
=
c
;
int
negate
=
0
;
if
((
b
&
0x80
)
!=
0
)
{
b
=
~
b
;
b
--
;
negate
=
1
;
}
ret
=
b
;
ret
=
(
negate
==
1
)
?
-
ret
:
ret
;
return
ret
;
}
/***********************************************************************
* aiptek_irq can receive one of six potential reports.
* The documentation for each is in the body of the function.
*
* The tablet reports on several attributes per invocation of
* aiptek_irq. Because the Linux Input Event system allows the
* transmission of ONE attribute per input_report_xxx() call,
* collation has to be done on the other end to reconstitute
* a complete tablet report. Further, the number of Input Event reports
* submitted varies, depending on what USB report type, and circumstance.
* To deal with this, EV_MSC is used to indicate an 'end-of-report'
* message. This has been an undocumented convention understood by the kernel
* tablet driver and clients such as gpm and XFree86's tablet drivers.
*
* Of the information received from the tablet, the one piece I
* cannot transmit is the proximity bit (without resorting to an EV_MSC
* convention above.) I therefore have taken over REL_MISC and ABS_MISC
* (for relative and absolute reports, respectively) for communicating
* Proximity. Why two events? I thought it interesting to know if the
* Proximity event occured while the tablet was in absolute or relative
* mode.
*
* Other tablets use the notion of a certain minimum stylus pressure
* to infer proximity. While that could have been done, that is yet
* another 'by convention' behavior, the documentation for which
* would be spread between two (or more) pieces of software.
*
* EV_MSC usage was terminated for this purpose in Linux 2.5.x, and
* replaced with the input_sync() method (which emits EV_SYN.)
*/
static
void
aiptek_irq
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
)
{
struct
aiptek
*
aiptek
=
urb
->
context
;
unsigned
char
*
data
=
aiptek
->
data
;
struct
input_dev
*
dev
=
&
aiptek
->
dev
;
int
x
;
int
y
;
int
pressure
;
int
proximity
;
int
retval
;
struct
input_dev
*
inputdev
=
&
aiptek
->
inputdev
;
int
jitterable
=
0
;
int
retval
,
macro
,
x
,
y
,
z
,
left
,
right
,
middle
,
p
,
dv
,
tip
,
bs
,
pck
;
switch
(
urb
->
status
)
{
case
0
:
/* success */
{
/* Success */
}
break
;
case
-
ECONNRESET
:
case
-
ENOENT
:
case
-
ESHUTDOWN
:
/* this urb is terminated, clean up */
dbg
(
"%s - urb shutting down with status: %d"
,
__FUNCTION__
,
urb
->
status
);
{
/* This urb is terminated, clean up */
dbg
(
"%s - urb shutting down with status: %d"
,
__FUNCTION__
,
urb
->
status
);
return
;
}
default:
dbg
(
"%s - nonzero urb status received: %d"
,
__FUNCTION__
,
urb
->
status
);
{
dbg
(
"%s - nonzero urb status received: %d"
,
__FUNCTION__
,
urb
->
status
);
goto
exit
;
}
}
/* See if we are in a delay loop -- throw out report if true.
*/
if
(
aiptek
->
inDelay
==
1
&&
time_after
(
aiptek
->
endDelay
,
jiffies
))
{
goto
exit
;
}
if
((
data
[
0
]
&
2
)
==
0
)
{
dbg
(
"received unknown report #%d"
,
data
[
0
]);
aiptek
->
inDelay
=
0
;
aiptek
->
eventCount
++
;
/* Report 1 delivers relative coordinates with either a stylus
* or the mouse. You do not know, however, which input
* tool generated the event.
*/
if
(
data
[
0
]
==
1
)
{
if
(
aiptek
->
curSetting
.
coordinateMode
==
AIPTEK_COORDINATE_ABSOLUTE_MODE
)
{
aiptek
->
diagnostic
=
AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE
;
}
else
{
input_regs
(
inputdev
,
regs
);
x
=
aiptek_convert_from_2s_complement
(
data
[
2
]);
y
=
aiptek_convert_from_2s_complement
(
data
[
3
]);
/* jitterable keeps track of whether any button has been pressed.
* We're also using it to remap the physical mouse button mask
* to pseudo-settings. (We don't specifically care about it's
* value after moving/transposing mouse button bitmasks, except
* that a non-zero value indicates that one or more
* mouse button was pressed.)
*/
jitterable
=
data
[
5
]
&
0x07
;
left
=
(
data
[
5
]
&
aiptek
->
curSetting
.
mouseButtonLeft
)
!=
0
?
1
:
0
;
right
=
(
data
[
5
]
&
aiptek
->
curSetting
.
mouseButtonRight
)
!=
0
?
1
:
0
;
middle
=
(
data
[
5
]
&
aiptek
->
curSetting
.
mouseButtonMiddle
)
!=
0
?
1
:
0
;
input_report_key
(
inputdev
,
BTN_LEFT
,
left
);
input_report_key
(
inputdev
,
BTN_MIDDLE
,
middle
);
input_report_key
(
inputdev
,
BTN_RIGHT
,
right
);
input_report_rel
(
inputdev
,
REL_X
,
x
);
input_report_rel
(
inputdev
,
REL_Y
,
y
);
input_report_rel
(
inputdev
,
REL_MISC
,
1
|
AIPTEK_REPORT_TOOL_UNKNOWN
);
/* Wheel support is in the form of a single-event
* firing.
*/
if
(
aiptek
->
curSetting
.
wheel
!=
AIPTEK_WHEEL_DISABLE
)
{
input_report_rel
(
inputdev
,
REL_WHEEL
,
aiptek
->
curSetting
.
wheel
);
aiptek
->
curSetting
.
wheel
=
AIPTEK_WHEEL_DISABLE
;
}
input_sync
(
inputdev
);
}
}
/* Report 2 is delivered only by the stylus, and delivers
* absolute coordinates.
*/
else
if
(
data
[
0
]
==
2
)
{
if
(
aiptek
->
curSetting
.
coordinateMode
==
AIPTEK_COORDINATE_RELATIVE_MODE
)
{
aiptek
->
diagnostic
=
AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE
;
}
else
if
(
!
AIPTEK_POINTER_ALLOW_STYLUS_MODE
(
aiptek
->
curSetting
.
pointerMode
))
{
aiptek
->
diagnostic
=
AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED
;
}
else
{
input_regs
(
inputdev
,
regs
);
x
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
1
)));
y
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
3
)));
z
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
6
)));
p
=
(
data
[
5
]
&
0x01
)
!=
0
?
1
:
0
;
dv
=
(
data
[
5
]
&
0x02
)
!=
0
?
1
:
0
;
tip
=
(
data
[
5
]
&
0x04
)
!=
0
?
1
:
0
;
/* Use jitterable to re-arrange button masks
*/
jitterable
=
data
[
5
]
&
0x18
;
bs
=
(
data
[
5
]
&
aiptek
->
curSetting
.
stylusButtonLower
)
!=
0
?
1
:
0
;
pck
=
(
data
[
5
]
&
aiptek
->
curSetting
.
stylusButtonUpper
)
!=
0
?
1
:
0
;
/* dv indicates 'data valid' (e.g., the tablet is in sync
* and has delivered a "correct" report) We will ignore
* all 'bad' reports...
*/
if
(
dv
!=
0
)
{
/* If we've not already sent a tool_button_?? code, do
* so now. Then set FIRED_BIT so it won't be resent unless
* the user forces FIRED_BIT off.
*/
if
(
TOOL_BUTTON_FIRED
(
aiptek
->
curSetting
.
toolMode
)
==
0
)
{
input_report_key
(
inputdev
,
TOOL_BUTTON
(
aiptek
->
curSetting
.
toolMode
),
1
);
aiptek
->
curSetting
.
toolMode
|=
TOOL_BUTTON_FIRED_BIT
;
}
if
(
p
!=
0
)
{
input_report_abs
(
inputdev
,
ABS_X
,
x
);
input_report_abs
(
inputdev
,
ABS_Y
,
y
);
input_report_abs
(
inputdev
,
ABS_PRESSURE
,
z
);
input_report_key
(
inputdev
,
BTN_TOUCH
,
tip
);
input_report_key
(
inputdev
,
BTN_STYLUS
,
bs
);
input_report_key
(
inputdev
,
BTN_STYLUS2
,
pck
);
if
(
aiptek
->
curSetting
.
xTilt
!=
AIPTEK_TILT_DISABLE
)
{
input_report_abs
(
inputdev
,
ABS_TILT_X
,
aiptek
->
curSetting
.
xTilt
);
}
if
(
aiptek
->
curSetting
.
yTilt
!=
AIPTEK_TILT_DISABLE
)
{
input_report_abs
(
inputdev
,
ABS_TILT_Y
,
aiptek
->
curSetting
.
yTilt
);
}
/* Wheel support is in the form of a single-event
* firing.
*/
if
(
aiptek
->
curSetting
.
wheel
!=
AIPTEK_WHEEL_DISABLE
)
{
input_report_abs
(
inputdev
,
ABS_WHEEL
,
aiptek
->
curSetting
.
wheel
);
aiptek
->
curSetting
.
wheel
=
AIPTEK_WHEEL_DISABLE
;
}
}
input_report_abs
(
inputdev
,
ABS_MISC
,
p
|
AIPTEK_REPORT_TOOL_STYLUS
);
input_sync
(
inputdev
);
}
}
}
/* Report 3's come from the mouse in absolute mode.
*/
else
if
(
data
[
0
]
==
3
)
{
if
(
aiptek
->
curSetting
.
coordinateMode
==
AIPTEK_COORDINATE_RELATIVE_MODE
)
{
aiptek
->
diagnostic
=
AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE
;
}
else
if
(
!
AIPTEK_POINTER_ALLOW_MOUSE_MODE
(
aiptek
->
curSetting
.
pointerMode
))
{
aiptek
->
diagnostic
=
AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED
;
}
else
{
input_regs
(
inputdev
,
regs
);
x
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
1
)));
y
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
3
)));
jitterable
=
data
[
5
]
&
0x1c
;
p
=
(
data
[
5
]
&
0x01
)
!=
0
?
1
:
0
;
dv
=
(
data
[
5
]
&
0x02
)
!=
0
?
1
:
0
;
left
=
(
data
[
5
]
&
aiptek
->
curSetting
.
mouseButtonLeft
)
!=
0
?
1
:
0
;
right
=
(
data
[
5
]
&
aiptek
->
curSetting
.
mouseButtonRight
)
!=
0
?
1
:
0
;
middle
=
(
data
[
5
]
&
aiptek
->
curSetting
.
mouseButtonMiddle
)
!=
0
?
1
:
0
;
if
(
dv
!=
0
)
{
/* If we've not already sent a tool_button_?? code, do
* so now. Then set FIRED_BIT so it won't be resent unless
* the user forces FIRED_BIT off.
*/
if
(
TOOL_BUTTON_FIRED
(
aiptek
->
curSetting
.
toolMode
)
==
0
)
{
input_report_key
(
inputdev
,
TOOL_BUTTON
(
aiptek
->
curSetting
.
toolMode
),
1
);
aiptek
->
curSetting
.
toolMode
|=
TOOL_BUTTON_FIRED_BIT
;
}
if
(
p
!=
0
)
{
input_report_abs
(
inputdev
,
ABS_X
,
x
);
input_report_abs
(
inputdev
,
ABS_Y
,
y
);
input_report_key
(
inputdev
,
BTN_LEFT
,
left
);
input_report_key
(
inputdev
,
BTN_MIDDLE
,
middle
);
input_report_key
(
inputdev
,
BTN_RIGHT
,
right
);
/* Wheel support is in the form of a single-event
* firing.
*/
if
(
aiptek
->
curSetting
.
wheel
!=
AIPTEK_WHEEL_DISABLE
)
{
input_report_abs
(
inputdev
,
ABS_WHEEL
,
aiptek
->
curSetting
.
wheel
);
aiptek
->
curSetting
.
wheel
=
AIPTEK_WHEEL_DISABLE
;
}
}
input_report_rel
(
inputdev
,
REL_MISC
,
p
|
AIPTEK_REPORT_TOOL_MOUSE
);
input_sync
(
inputdev
);
}
}
}
/* Report 4s come from the macro keys when pressed by stylus
*/
else
if
(
data
[
0
]
==
4
)
{
jitterable
=
data
[
1
]
&
0x18
;
p
=
(
data
[
1
]
&
0x01
)
!=
0
?
1
:
0
;
dv
=
(
data
[
1
]
&
0x02
)
!=
0
?
1
:
0
;
tip
=
(
data
[
1
]
&
0x04
)
!=
0
?
1
:
0
;
bs
=
(
data
[
1
]
&
aiptek
->
curSetting
.
stylusButtonLower
)
!=
0
?
1
:
0
;
pck
=
(
data
[
1
]
&
aiptek
->
curSetting
.
stylusButtonUpper
)
!=
0
?
1
:
0
;
macro
=
data
[
3
];
z
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
4
)));
if
(
dv
!=
0
)
{
input_regs
(
inputdev
,
regs
);
/* If we've not already sent a tool_button_?? code, do
* so now. Then set FIRED_BIT so it won't be resent unless
* the user forces FIRED_BIT off.
*/
if
(
TOOL_BUTTON_FIRED
(
aiptek
->
curSetting
.
toolMode
)
==
0
)
{
input_report_key
(
inputdev
,
TOOL_BUTTON
(
aiptek
->
curSetting
.
toolMode
),
1
);
aiptek
->
curSetting
.
toolMode
|=
TOOL_BUTTON_FIRED_BIT
;
}
if
(
p
!=
0
)
{
input_report_key
(
inputdev
,
BTN_TOUCH
,
tip
);
input_report_key
(
inputdev
,
BTN_STYLUS
,
bs
);
input_report_key
(
inputdev
,
BTN_STYLUS2
,
pck
);
input_report_abs
(
inputdev
,
ABS_PRESSURE
,
z
);
}
/* For safety, we're sending key 'break' codes for the
* neighboring macro keys.
*/
if
(
macro
>
0
)
{
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
-
1
],
0
);
}
if
(
macro
<
25
)
{
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
+
1
],
0
);
}
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
],
p
);
input_report_abs
(
inputdev
,
ABS_MISC
,
p
|
AIPTEK_REPORT_TOOL_STYLUS
);
input_sync
(
inputdev
);
}
}
/* Report 5s come from the macro keys when pressed by mouse
*/
else
if
(
data
[
0
]
==
5
)
{
jitterable
=
data
[
1
]
&
0x1c
;
p
=
(
data
[
1
]
&
0x01
)
!=
0
?
1
:
0
;
dv
=
(
data
[
1
]
&
0x02
)
!=
0
?
1
:
0
;
left
=
(
data
[
1
]
&
aiptek
->
curSetting
.
mouseButtonLeft
)
!=
0
?
1
:
0
;
right
=
(
data
[
1
]
&
aiptek
->
curSetting
.
mouseButtonRight
)
!=
0
?
1
:
0
;
middle
=
(
data
[
1
]
&
aiptek
->
curSetting
.
mouseButtonMiddle
)
!=
0
?
1
:
0
;
macro
=
data
[
3
];
if
(
dv
!=
0
)
{
input_regs
(
inputdev
,
regs
);
/* If we've not already sent a tool_button_?? code, do
* so now. Then set FIRED_BIT so it won't be resent unless
* the user forces FIRED_BIT off.
*/
if
(
TOOL_BUTTON_FIRED
(
aiptek
->
curSetting
.
toolMode
)
==
0
)
{
input_report_key
(
inputdev
,
TOOL_BUTTON
(
aiptek
->
curSetting
.
toolMode
),
1
);
aiptek
->
curSetting
.
toolMode
|=
TOOL_BUTTON_FIRED_BIT
;
}
if
(
p
!=
0
)
{
input_report_key
(
inputdev
,
BTN_LEFT
,
left
);
input_report_key
(
inputdev
,
BTN_MIDDLE
,
middle
);
input_report_key
(
inputdev
,
BTN_RIGHT
,
right
);
}
input_regs
(
dev
,
regs
);
/* For safety, we're sending key 'break' codes for the
* neighboring macro keys.
*/
if
(
macro
>
0
)
{
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
-
1
],
0
);
}
if
(
macro
<
25
)
{
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
+
1
],
0
);
}
proximity
=
data
[
5
]
&
0x01
;
input_report_key
(
dev
,
BTN_TOOL_PEN
,
proximity
);
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
],
1
);
input_report_rel
(
inputdev
,
ABS_MISC
,
p
|
AIPTEK_REPORT_TOOL_MOUSE
);
input_sync
(
inputdev
);
}
}
/* We have no idea which tool can generate a report 6. Theoretically,
* neither need to, having been given reports 4 & 5 for such use.
* However, report 6 is the 'official-looking' report for macroKeys;
* reports 4 & 5 supposively are used to support unnamed, unknown
* hat switches (which just so happen to be the macroKeys.)
*/
else
if
(
data
[
0
]
==
6
)
{
macro
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
data
+
1
)));
input_regs
(
inputdev
,
regs
);
x
=
le16_to_cpu
(
get_unaligned
((
u16
*
)
&
data
[
1
]));
y
=
le16_to_cpu
(
get_unaligned
((
u16
*
)
&
data
[
3
]));
pressure
=
le16_to_cpu
(
*
(
u16
*
)
&
data
[
6
]);
pressure
-=
aiptek
->
features
->
pressure_min
;
if
(
macro
>
0
)
{
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
-
1
],
0
);
}
if
(
macro
<
25
)
{
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
+
1
],
0
);
}
if
(
pressure
<
0
)
{
pressure
=
0
;
/* If we've not already sent a tool_button_?? code, do
* so now. Then set FIRED_BIT so it won't be resent unless
* the user forces FIRED_BIT off.
*/
if
(
TOOL_BUTTON_FIRED
(
aiptek
->
curSetting
.
toolMode
)
==
0
)
{
input_report_key
(
inputdev
,
TOOL_BUTTON
(
aiptek
->
curSetting
.
toolMode
),
1
);
aiptek
->
curSetting
.
toolMode
|=
TOOL_BUTTON_FIRED_BIT
;
}
if
(
proximity
)
{
input_report_abs
(
dev
,
ABS_X
,
x
);
input_report_abs
(
dev
,
ABS_Y
,
y
);
input_report_abs
(
dev
,
ABS_PRESSURE
,
pressure
);
input_report_key
(
dev
,
BTN_TOUCH
,
data
[
5
]
&
0x04
);
input_report_key
(
dev
,
BTN_STYLUS
,
data
[
5
]
&
0x08
);
input_report_key
(
dev
,
BTN_STYLUS2
,
data
[
5
]
&
0x10
);
input_report_key
(
inputdev
,
macroKeyEvents
[
macro
],
1
);
input_report_abs
(
inputdev
,
ABS_MISC
,
1
|
AIPTEK_REPORT_TOOL_UNKNOWN
);
input_sync
(
inputdev
);
}
else
{
dbg
(
"Unknown report %d"
,
data
[
0
]);
}
input_sync
(
dev
);
/* Jitter may occur when the user presses a button on the stlyus
* or the mouse. What we do to prevent that is wait 'x' milliseconds
* following a 'jitterable' event, which should give the hand some time
* stabilize itself.
*
* We just introduced aiptek->previousJitterable to carry forth the
* notion that jitter occurs when the button state changes from on to off:
* a person drawing, holding a button down is not subject to jittering.
* With that in mind, changing from upper button depressed to lower button
* WILL transition through a jitter delay.
*/
if
(
aiptek
->
previousJitterable
!=
jitterable
&&
aiptek
->
curSetting
.
jitterDelay
!=
0
&&
aiptek
->
inDelay
!=
1
)
{
aiptek
->
endDelay
=
jiffies
+
((
aiptek
->
curSetting
.
jitterDelay
*
HZ
)
/
1000
);
aiptek
->
inDelay
=
1
;
}
aiptek
->
previousJitterable
=
jitterable
;
exit:
retval
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
retval
)
err
(
"%s - usb_submit_urb failed with result %d"
,
exit:
retval
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
retval
!=
0
)
{
err
(
"%s - usb_submit_urb failed with result %d"
,
__FUNCTION__
,
retval
);
}
}
static
struct
aiptek_features
aiptek_features
[]
=
{
{
"Aiptek 6000U/8000U"
,
8
,
3000
,
2250
,
26
,
511
,
aiptek_irq
,
0
,
0
,
0
,
0
},
{
NULL
,
0
}
};
static
struct
usb_device_id
aiptek_ids
[]
=
{
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x20
),
.
driver_info
=
0
},
/***********************************************************************
* These are the USB id's known so far. We do not identify them to
* specific Aiptek model numbers, because there has been overlaps,
* use, and reuse of id's in existing models. Certain models have
* been known to use more than one ID, indicative perhaps of
* manufacturing revisions. In any event, we consider these
* IDs to not be model-specific nor unique.
*/
struct
usb_device_id
aiptek_ids
[]
=
{
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x01
)},
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x10
)},
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x20
)},
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x21
)},
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x22
)},
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x23
)},
{
USB_DEVICE
(
USB_VENDOR_ID_AIPTEK
,
0x24
)},
{}
};
MODULE_DEVICE_TABLE
(
usb
,
aiptek_ids
);
static
int
aiptek_open
(
struct
input_dev
*
dev
)
/***********************************************************************
* Open an instance of the tablet driver.
*/
static
int
aiptek_open
(
struct
input_dev
*
inputdev
)
{
struct
aiptek
*
aiptek
=
dev
->
private
;
struct
aiptek
*
aiptek
=
input
dev
->
private
;
if
(
aiptek
->
open
++
)
if
(
aiptek
->
open
Count
++
>
0
)
{
return
0
;
}
aiptek
->
irq
->
dev
=
aiptek
->
usbdev
;
if
(
usb_submit_urb
(
aiptek
->
irq
,
GFP_KERNEL
)
)
{
aiptek
->
open
--
;
aiptek
->
urb
->
dev
=
aiptek
->
usbdev
;
if
(
usb_submit_urb
(
aiptek
->
urb
,
GFP_KERNEL
)
!=
0
)
{
aiptek
->
open
Count
--
;
return
-
EIO
;
}
return
0
;
}
static
void
aiptek_close
(
struct
input_dev
*
dev
)
/***********************************************************************
* Close an instance of the tablet driver.
*/
static
void
aiptek_close
(
struct
input_dev
*
inputdev
)
{
struct
aiptek
*
aiptek
=
dev
->
private
;
struct
aiptek
*
aiptek
=
input
dev
->
private
;
if
(
!--
aiptek
->
open
)
usb_unlink_urb
(
aiptek
->
irq
);
if
(
--
aiptek
->
openCount
==
0
)
{
usb_unlink_urb
(
aiptek
->
urb
);
}
}
/***********************************************************************
* aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x,
* where they were known as usb_set_report and usb_get_report.
*/
static
int
aiptek_set_report
(
struct
aiptek
*
aiptek
,
unsigned
char
report_type
,
unsigned
char
report_id
,
void
*
buffer
,
int
size
)
{
return
usb_control_msg
(
aiptek
->
usbdev
,
usb_sndctrlpipe
(
aiptek
->
usbdev
,
0
),
USB_REQ_SET_REPORT
,
USB_TYPE_CLASS
|
USB_RECIP_INTERFACE
|
USB_DIR_OUT
,
(
report_type
<<
8
)
+
report_id
,
aiptek
->
ifnum
,
buffer
,
size
,
5
*
HZ
);
}
#define USB_REQ_SET_REPORT 0x09
static
int
usb_set_report
(
struct
usb_device
*
dev
,
struct
usb_host_interface
*
inter
,
unsigned
char
type
,
unsigned
char
id
,
void
*
buf
,
int
size
)
aiptek_get_report
(
struct
aiptek
*
aiptek
,
unsigned
char
report_type
,
unsigned
char
report_id
,
void
*
buffer
,
int
size
)
{
return
usb_control_msg
(
dev
,
usb_sndctrlpipe
(
dev
,
0
),
USB_REQ_SET_REPORT
,
USB_TYPE_CLASS
|
USB_RECIP_INTERFACE
,
(
type
<<
8
)
+
id
,
inter
->
desc
.
bInterfaceNumber
,
buf
,
size
,
HZ
);
return
usb_control_msg
(
aiptek
->
usbdev
,
usb_rcvctrlpipe
(
aiptek
->
usbdev
,
0
),
USB_REQ_GET_REPORT
,
USB_TYPE_CLASS
|
USB_RECIP_INTERFACE
|
USB_DIR_IN
,
(
report_type
<<
8
)
+
report_id
,
aiptek
->
ifnum
,
buffer
,
size
,
5
*
HZ
);
}
/***********************************************************************
* Send a command to the tablet.
*/
static
int
aiptek_command
(
struct
usb_device
*
dev
,
struct
usb_host_interface
*
inter
,
unsigned
char
command
,
unsigned
char
data
)
aiptek_command
(
struct
aiptek
*
aiptek
,
unsigned
char
command
,
unsigned
char
data
)
{
const
int
sizeof_buf
=
3
*
sizeof
(
u8
);
int
ret
;
u8
*
buf
;
int
err
;
buf
=
kmalloc
(
3
,
GFP_KERNEL
);
if
(
!
buf
)
buf
=
kmalloc
(
sizeof_buf
,
GFP_KERNEL
);
if
(
!
buf
)
{
return
-
ENOMEM
;
}
buf
[
0
]
=
4
;
buf
[
0
]
=
2
;
buf
[
1
]
=
command
;
buf
[
2
]
=
data
;
if
((
err
=
usb_set_report
(
dev
,
inter
,
3
,
2
,
buf
,
3
))
!=
3
)
{
dbg
(
"aiptek_command: 0x%x 0x%x
\n
"
,
command
,
data
);
if
((
ret
=
aiptek_set_report
(
aiptek
,
3
,
2
,
buf
,
sizeof_buf
))
!=
sizeof_buf
)
{
dbg
(
"aiptek_program: failed, tried to send: 0x%02x 0x%02x"
,
command
,
data
);
}
kfree
(
buf
);
return
err
<
0
?
err
:
0
;
return
ret
<
0
?
ret
:
0
;
}
/***********************************************************************
* Retrieve information from the tablet. Querying info is defined as first
* sending the {command,data} sequence as a command, followed by a wait
* (aka, "programmaticDelay") and then a "read" request.
*/
static
int
aiptek_probe
(
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
)
aiptek_query
(
struct
aiptek
*
aiptek
,
unsigned
char
command
,
unsigned
char
data
)
{
struct
usb_device
*
dev
=
interface_to_usbdev
(
intf
);
struct
usb_host_interface
*
interface
=
intf
->
cur_altsetting
;
struct
usb_endpoint_descriptor
*
endpoint
;
struct
aiptek
*
aiptek
;
int
err
=
-
ENOMEM
;
const
int
sizeof_buf
=
3
*
sizeof
(
u8
);
int
ret
;
u8
*
buf
;
if
(
!
(
aiptek
=
kmalloc
(
sizeof
(
struct
aiptek
),
GFP_KERNEL
)))
goto
error_out_noalloc
;
buf
=
kmalloc
(
sizeof_buf
,
GFP_KERNEL
);
if
(
!
buf
)
{
return
-
ENOMEM
;
}
memset
(
aiptek
,
0
,
sizeof
(
struct
aiptek
));
buf
[
0
]
=
2
;
buf
[
1
]
=
command
;
buf
[
2
]
=
data
;
aiptek
->
data
=
usb_buffer_alloc
(
dev
,
10
,
GFP_KERNEL
,
&
aiptek
->
data_dma
);
if
(
!
aiptek
->
data
)
{
goto
error_out_nobuf
;
if
(
aiptek_command
(
aiptek
,
command
,
data
)
!=
0
)
{
kfree
(
buf
);
return
-
EIO
;
}
aiptek
->
irq
=
usb_alloc_urb
(
0
,
GFP_KERNEL
);
if
(
!
aiptek
->
irq
)
{
goto
error_out_nourb
;
wait_ms
(
aiptek
->
curSetting
.
programmableDelay
);
if
((
ret
=
aiptek_get_report
(
aiptek
,
3
,
2
,
buf
,
sizeof_buf
))
!=
sizeof_buf
)
{
dbg
(
"aiptek_query failed: returned 0x%02x 0x%02x 0x%02x"
,
buf
[
0
],
buf
[
1
],
buf
[
2
]);
ret
=
-
EIO
;
}
else
{
ret
=
le16_to_cpu
(
get_unaligned
((
__u16
*
)
(
buf
+
1
)));
}
kfree
(
buf
);
return
ret
;
}
/* Resolution500LPI */
err
=
aiptek_command
(
dev
,
interface
,
0x18
,
0x04
);
if
(
err
)
goto
error_out
;
/***********************************************************************
* Program the tablet into either absolute or relative mode.
* We also get information about the tablet's size.
*/
static
int
aiptek_program_tablet
(
struct
aiptek
*
aiptek
)
{
int
ret
;
/* Execute Resolution500LPI */
if
((
ret
=
aiptek_command
(
aiptek
,
0x18
,
0x04
))
<
0
)
{
return
ret
;
}
/* SwitchToTablet */
err
=
aiptek_command
(
dev
,
interface
,
0x10
,
0x01
);
if
(
err
)
goto
error_out
;
/* Query getModelCode */
if
((
ret
=
aiptek_query
(
aiptek
,
0x02
,
0x00
))
<
0
)
{
return
ret
;
}
aiptek
->
features
.
modelCode
=
ret
&
0xff
;
aiptek
->
features
=
aiptek_features
+
id
->
driver_info
;
/* Query getODMCode */
if
((
ret
=
aiptek_query
(
aiptek
,
0x03
,
0x00
))
<
0
)
{
return
ret
;
}
aiptek
->
features
.
odmCode
=
ret
;
aiptek
->
dev
.
evbit
[
0
]
|=
BIT
(
EV_KEY
)
|
BIT
(
EV_ABS
)
|
BIT
(
EV_MSC
)
|
aiptek
->
features
->
evbit
;
/* Query getFirmwareCode */
if
((
ret
=
aiptek_query
(
aiptek
,
0x04
,
0x00
))
<
0
)
{
return
ret
;
}
aiptek
->
features
.
firmwareCode
=
ret
;
aiptek
->
dev
.
absbit
[
0
]
|=
BIT
(
ABS_X
)
|
BIT
(
ABS_Y
)
|
BIT
(
ABS_PRESSURE
)
|
BIT
(
ABS_MISC
)
|
aiptek
->
features
->
absbit
;
/* Query getXextension */
if
((
ret
=
aiptek_query
(
aiptek
,
0x01
,
0x00
))
<
0
)
{
return
ret
;
}
aiptek
->
inputdev
.
absmin
[
ABS_X
]
=
0
;
aiptek
->
inputdev
.
absmax
[
ABS_X
]
=
ret
-
1
;
aiptek
->
dev
.
relbit
[
0
]
|=
aiptek
->
features
->
relbit
;
/* Query getYextension */
if
((
ret
=
aiptek_query
(
aiptek
,
0x01
,
0x01
))
<
0
)
{
return
ret
;
}
aiptek
->
inputdev
.
absmin
[
ABS_Y
]
=
0
;
aiptek
->
inputdev
.
absmax
[
ABS_Y
]
=
ret
-
1
;
aiptek
->
dev
.
keybit
[
LONG
(
BTN_LEFT
)]
|=
BIT
(
BTN_LEFT
)
|
BIT
(
BTN_RIGHT
)
|
BIT
(
BTN_MIDDLE
)
|
aiptek
->
features
->
btnbit
;
/* Query getPressureLevels */
if
((
ret
=
aiptek_query
(
aiptek
,
0x08
,
0x00
))
<
0
)
{
return
ret
;
}
aiptek
->
inputdev
.
absmin
[
ABS_PRESSURE
]
=
0
;
aiptek
->
inputdev
.
absmax
[
ABS_PRESSURE
]
=
ret
-
1
;
aiptek
->
dev
.
keybit
[
LONG
(
BTN_DIGI
)]
|=
BIT
(
BTN_TOOL_PEN
)
|
BIT
(
BTN_TOOL_MOUSE
)
|
BIT
(
BTN_TOUCH
)
|
BIT
(
BTN_STYLUS
)
|
BIT
(
BTN_STYLUS2
)
|
aiptek
->
features
->
digibit
;
/* Depending on whether we are in absolute or relative mode, we will
* do a switchToTablet(absolute) or switchToMouse(relative) command.
*/
if
(
aiptek
->
curSetting
.
coordinateMode
==
AIPTEK_COORDINATE_ABSOLUTE_MODE
)
{
/* Execute switchToTablet */
if
((
ret
=
aiptek_command
(
aiptek
,
0x10
,
0x01
))
<
0
)
{
return
ret
;
}
}
else
{
/* Execute switchToMouse */
if
((
ret
=
aiptek_command
(
aiptek
,
0x10
,
0x00
))
<
0
)
{
return
ret
;
}
}
aiptek
->
dev
.
mscbit
[
0
]
=
BIT
(
MSC_SERIAL
);
/* Enable the macro keys */
if
((
ret
=
aiptek_command
(
aiptek
,
0x11
,
0x02
))
<
0
)
{
return
ret
;
}
#if 0
/* Execute FilterOn */
if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0) {
return ret;
}
#endif
aiptek
->
dev
.
absmax
[
ABS_X
]
=
aiptek
->
features
->
x_max
;
aiptek
->
dev
.
absmax
[
ABS_Y
]
=
aiptek
->
features
->
y_max
;
aiptek
->
dev
.
absmax
[
ABS_PRESSURE
]
=
aiptek
->
features
->
pressure_max
-
aiptek
->
features
->
pressure_min
;
/* Execute AutoGainOn */
if
((
ret
=
aiptek_command
(
aiptek
,
0x12
,
0xff
))
<
0
)
{
return
ret
;
}
aiptek
->
dev
.
absfuzz
[
ABS_X
]
=
0
;
aiptek
->
dev
.
absfuzz
[
ABS_Y
]
=
0
;
/* Reset the eventCount, so we track events from last (re)programming
*/
aiptek
->
diagnostic
=
AIPTEK_DIAGNOSTIC_NA
;
aiptek
->
eventCount
=
0
;
aiptek
->
dev
.
private
=
aiptek
;
aiptek
->
dev
.
open
=
aiptek_open
;
aiptek
->
dev
.
close
=
aiptek_close
;
return
0
;
}
aiptek
->
dev
.
name
=
aiptek
->
features
->
name
;
aiptek
->
dev
.
id
.
bustype
=
BUS_USB
;
aiptek
->
dev
.
id
.
vendor
=
dev
->
descriptor
.
idVendor
;
aiptek
->
dev
.
id
.
product
=
dev
->
descriptor
.
idProduct
;
aiptek
->
dev
.
id
.
version
=
dev
->
descriptor
.
bcdDevice
;
aiptek
->
dev
.
dev
=
&
intf
->
dev
;
aiptek
->
usbdev
=
dev
;
/***********************************************************************
* Sysfs functions. Sysfs prefers that individually-tunable parameters
* exist in their separate pseudo-files. Summary data that is immutable
* may exist in a singular file so long as you don't define a writeable
* interface.
*/
endpoint
=
&
intf
->
altsetting
[
0
].
endpoint
[
0
].
desc
;
/***********************************************************************
* support the 'size' file -- display support
*/
static
ssize_t
show_tabletSize
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
->
features
->
pktlen
>
10
)
BUG
();
if
(
aiptek
==
NULL
)
{
return
0
;
}
usb_fill_int_urb
(
aiptek
->
irq
,
dev
,
usb_rcvintpipe
(
dev
,
endpoint
->
bEndpointAddress
),
aiptek
->
data
,
aiptek
->
features
->
pktlen
,
aiptek
->
features
->
irq
,
aiptek
,
endpoint
->
bInterval
);
aiptek
->
irq
->
transfer_dma
=
aiptek
->
data_dma
;
aiptek
->
irq
->
transfer_flags
|=
URB_NO_TRANSFER_DMA_MAP
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"%dx%d
\n
"
,
aiptek
->
inputdev
.
absmax
[
ABS_X
]
+
1
,
aiptek
->
inputdev
.
absmax
[
ABS_Y
]
+
1
);
}
input_register_device
(
&
aiptek
->
dev
);
/* These structs define the sysfs files, param #1 is the name of the
* file, param 2 is the file permissions, param 3 & 4 are to the
* output generator and input parser routines. Absence of a routine is
* permitted -- it only means can't either 'cat' the file, or send data
* to it.
*/
static
DEVICE_ATTR
(
size
,
S_IRUGO
,
show_tabletSize
,
NULL
);
printk
(
KERN_INFO
"input: %s on usb%d:%d
\n
"
,
aiptek
->
features
->
name
,
dev
->
bus
->
busnum
,
dev
->
devnum
);
/***********************************************************************
* support routines for the 'product_id' file
*/
static
ssize_t
show_tabletProductId
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
usb_set_intfdata
(
intf
,
aiptek
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
error_out:
usb_free_urb
(
aiptek
->
irq
);
error_out_nourb:
usb_buffer_free
(
dev
,
10
,
aiptek
->
data
,
aiptek
->
data_dma
);
error_out_nobuf:
kfree
(
aiptek
);
error_out_noalloc:
return
err
;
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%04x
\n
"
,
aiptek
->
inputdev
.
id
.
product
);
}
static
void
aiptek_disconnect
(
struct
usb_interface
*
intf
)
static
DEVICE_ATTR
(
product_id
,
S_IRUGO
,
show_tabletProductId
,
NULL
);
/***********************************************************************
* support routines for the 'vendor_id' file
*/
static
ssize_t
show_tabletVendorId
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
usb_get_intfdata
(
intf
);
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
usb_set_intfdata
(
intf
,
NULL
);
if
(
aiptek
)
{
usb_unlink_urb
(
aiptek
->
irq
);
input_unregister_device
(
&
aiptek
->
dev
);
usb_free_urb
(
aiptek
->
irq
);
usb_buffer_free
(
interface_to_usbdev
(
intf
),
10
,
aiptek
->
data
,
aiptek
->
data_dma
);
kfree
(
aiptek
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%04x
\n
"
,
aiptek
->
inputdev
.
id
.
vendor
);
}
static
struct
usb_driver
aiptek_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"aiptek"
,
.
probe
=
aiptek_probe
,
.
disconnect
=
aiptek_disconnect
,
.
id_table
=
aiptek_ids
,
};
static
DEVICE_ATTR
(
vendor_id
,
S_IRUGO
,
show_tabletVendorId
,
NULL
);
static
int
__init
aiptek_init
(
void
)
/***********************************************************************
* support routines for the 'vendor' file
*/
static
ssize_t
show_tabletManufacturer
(
struct
device
*
dev
,
char
*
buf
)
{
int
result
=
usb_register
(
&
aiptek_driver
);
if
(
result
==
0
)
{
info
(
DRIVER_VERSION
" "
DRIVER_AUTHOR
);
info
(
DRIVER_DESC
);
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
int
retval
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
result
;
retval
=
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
aiptek
->
features
.
manuName
);
return
retval
;
}
static
void
__exit
aiptek_exit
(
void
)
static
DEVICE_ATTR
(
vendor
,
S_IRUGO
,
show_tabletManufacturer
,
NULL
);
/***********************************************************************
* support routines for the 'product' file
*/
static
ssize_t
show_tabletProduct
(
struct
device
*
dev
,
char
*
buf
)
{
usb_deregister
(
&
aiptek_driver
);
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
int
retval
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
retval
=
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
aiptek
->
features
.
prodName
);
return
retval
;
}
static
DEVICE_ATTR
(
product
,
S_IRUGO
,
show_tabletProduct
,
NULL
);
/***********************************************************************
* support routines for the 'pointer_mode' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletPointerMode
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
pointerMode
)
{
case
AIPTEK_POINTER_ONLY_STYLUS_MODE
:
{
s
=
"stylus"
;
break
;
}
case
AIPTEK_POINTER_ONLY_MOUSE_MODE
:
{
s
=
"mouse"
;
break
;
}
case
AIPTEK_POINTER_EITHER_MODE
:
{
s
=
"either"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletPointerMode
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"stylus"
)
==
0
)
{
aiptek
->
newSetting
.
pointerMode
=
AIPTEK_POINTER_ONLY_STYLUS_MODE
;
}
else
if
(
strcmp
(
buf
,
"mouse"
)
==
0
)
{
aiptek
->
newSetting
.
pointerMode
=
AIPTEK_POINTER_ONLY_MOUSE_MODE
;
}
else
if
(
strcmp
(
buf
,
"either"
)
==
0
)
{
aiptek
->
newSetting
.
pointerMode
=
AIPTEK_POINTER_EITHER_MODE
;
}
return
count
;
}
static
DEVICE_ATTR
(
pointer_mode
,
S_IRUGO
|
S_IWUGO
,
show_tabletPointerMode
,
store_tabletPointerMode
);
/***********************************************************************
* support routines for the 'coordinate_mode' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletCoordinateMode
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
coordinateMode
)
{
case
AIPTEK_COORDINATE_ABSOLUTE_MODE
:
{
s
=
"absolute"
;
break
;
}
case
AIPTEK_COORDINATE_RELATIVE_MODE
:
{
s
=
"relative"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletCoordinateMode
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"absolute"
)
==
0
)
{
aiptek
->
newSetting
.
pointerMode
=
AIPTEK_COORDINATE_ABSOLUTE_MODE
;
}
else
if
(
strcmp
(
buf
,
"relative"
)
==
0
)
{
aiptek
->
newSetting
.
pointerMode
=
AIPTEK_COORDINATE_RELATIVE_MODE
;
}
return
count
;
}
static
DEVICE_ATTR
(
coordinate_mode
,
S_IRUGO
|
S_IWUGO
,
show_tabletCoordinateMode
,
store_tabletCoordinateMode
);
/***********************************************************************
* support routines for the 'tool_mode' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletToolMode
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
TOOL_BUTTON
(
aiptek
->
curSetting
.
toolMode
))
{
case
AIPTEK_TOOL_BUTTON_MOUSE_MODE
:
{
s
=
"mouse"
;
break
;
}
case
AIPTEK_TOOL_BUTTON_ERASER_MODE
:
{
s
=
"eraser"
;
break
;
}
case
AIPTEK_TOOL_BUTTON_PENCIL_MODE
:
{
s
=
"pencil"
;
break
;
}
case
AIPTEK_TOOL_BUTTON_PEN_MODE
;
{
s
=
"pen"
;
break
;
}
case
AIPTEK_TOOL_BUTTON_BRUSH_MODE
:
{
s
=
"brush"
;
break
;
}
case
AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE
:
{
s
=
"airbrush"
;
break
;
}
case
AIPTEK_TOOL_BUTTON_LENS_MODE
:
{
s
=
"lens"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletToolMode
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"mouse"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_MOUSE_MODE
;
}
else
if
(
strcmp
(
buf
,
"eraser"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_ERASER_MODE
;
}
else
if
(
strcmp
(
buf
,
"pencil"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_PENCIL_MODE
;
}
else
if
(
strcmp
(
buf
,
"pen"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_PEN_MODE
;
}
else
if
(
strcmp
(
buf
,
"brush"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_BRUSH_MODE
;
}
else
if
(
strcmp
(
buf
,
"airbrush"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE
;
}
else
if
(
strcmp
(
buf
,
"lens"
)
==
0
)
{
aiptek
->
newSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_LENS_MODE
;
}
return
count
;
}
static
DEVICE_ATTR
(
tool_mode
,
S_IRUGO
|
S_IWUGO
,
show_tabletToolMode
,
store_tabletToolMode
);
/***********************************************************************
* support routines for the 'xtilt' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletXtilt
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
aiptek
->
curSetting
.
xTilt
==
AIPTEK_TILT_DISABLE
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"disable
\n
"
);
}
else
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
aiptek
->
curSetting
.
xTilt
);
}
}
static
ssize_t
store_tabletXtilt
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
int
x
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"disable"
)
==
0
)
{
aiptek
->
newSetting
.
xTilt
=
AIPTEK_TILT_DISABLE
;
}
else
{
x
=
(
int
)
simple_strtol
(
buf
,
0
,
10
);
if
(
x
>=
AIPTEK_TILT_MIN
&&
x
<=
AIPTEK_TILT_MAX
)
{
aiptek
->
newSetting
.
xTilt
=
x
;
}
}
return
count
;
}
static
DEVICE_ATTR
(
xtilt
,
S_IRUGO
|
S_IWUGO
,
show_tabletXtilt
,
store_tabletXtilt
);
/***********************************************************************
* support routines for the 'ytilt' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletYtilt
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
aiptek
->
curSetting
.
yTilt
==
AIPTEK_TILT_DISABLE
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"disable
\n
"
);
}
else
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
aiptek
->
curSetting
.
yTilt
);
}
}
static
ssize_t
store_tabletYtilt
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
int
y
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"disable"
)
==
0
)
{
aiptek
->
newSetting
.
yTilt
=
AIPTEK_TILT_DISABLE
;
}
else
{
y
=
(
int
)
simple_strtol
(
buf
,
0
,
10
);
if
(
y
>=
AIPTEK_TILT_MIN
&&
y
<=
AIPTEK_TILT_MAX
)
{
aiptek
->
newSetting
.
yTilt
=
y
;
}
}
return
count
;
}
static
DEVICE_ATTR
(
ytilt
,
S_IRUGO
|
S_IWUGO
,
show_tabletYtilt
,
store_tabletYtilt
);
/***********************************************************************
* support routines for the 'jitter' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletJitterDelay
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
aiptek
->
curSetting
.
jitterDelay
);
}
static
ssize_t
store_tabletJitterDelay
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
aiptek
->
newSetting
.
jitterDelay
=
(
int
)
simple_strtol
(
buf
,
0
,
10
);
return
count
;
}
static
DEVICE_ATTR
(
jitter
,
S_IRUGO
|
S_IWUGO
,
show_tabletJitterDelay
,
store_tabletJitterDelay
);
/***********************************************************************
* support routines for the 'delay' file. Note that this file
* both displays current setting and allows reprogramming.
*/
static
ssize_t
show_tabletProgrammableDelay
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
aiptek
->
curSetting
.
programmableDelay
);
}
static
ssize_t
store_tabletProgrammableDelay
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
aiptek
->
newSetting
.
programmableDelay
=
(
int
)
simple_strtol
(
buf
,
0
,
10
);
return
count
;
}
static
DEVICE_ATTR
(
delay
,
S_IRUGO
|
S_IWUGO
,
show_tabletProgrammableDelay
,
store_tabletProgrammableDelay
);
/***********************************************************************
* support routines for the 'input_path' file. Note that this file
* only displays current setting.
*/
static
ssize_t
show_tabletInputDevice
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"/dev/input/%s
\n
"
,
aiptek
->
features
.
inputPath
);
}
static
DEVICE_ATTR
(
input_path
,
S_IRUGO
,
show_tabletInputDevice
,
NULL
);
/***********************************************************************
* support routines for the 'event_count' file. Note that this file
* only displays current setting.
*/
static
ssize_t
show_tabletEventsReceived
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%ld
\n
"
,
aiptek
->
eventCount
);
}
static
DEVICE_ATTR
(
event_count
,
S_IRUGO
,
show_tabletEventsReceived
,
NULL
);
/***********************************************************************
* support routines for the 'diagnostic' file. Note that this file
* only displays current setting.
*/
static
ssize_t
show_tabletDiagnosticMessage
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
retMsg
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
diagnostic
)
{
case
AIPTEK_DIAGNOSTIC_NA
:
{
retMsg
=
"no errors
\n
"
;
break
;
}
case
AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE
:
{
retMsg
=
"Error: receiving relative reports
\n
"
;
break
;
}
case
AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE
:
{
retMsg
=
"Error: receiving absolute reports
\n
"
;
break
;
}
case
AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED
:
{
if
(
aiptek
->
curSetting
.
pointerMode
==
AIPTEK_POINTER_ONLY_MOUSE_MODE
)
{
retMsg
=
"Error: receiving stylus reports
\n
"
;
}
else
{
retMsg
=
"Error: receiving mouse reports
\n
"
;
}
break
;
}
default:
{
return
0
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
retMsg
);
}
static
DEVICE_ATTR
(
diagnostic
,
S_IRUGO
,
show_tabletDiagnosticMessage
,
NULL
);
/***********************************************************************
* support routines for the 'stylus_upper' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletStylusUpper
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
stylusButtonUpper
)
{
case
AIPTEK_STYLUS_UPPER_BUTTON
:
{
s
=
"upper"
;
break
;
}
case
AIPTEK_STYLUS_LOWER_BUTTON
:
{
s
=
"lower"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletStylusUpper
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"upper"
)
==
0
)
{
aiptek
->
newSetting
.
stylusButtonUpper
=
AIPTEK_STYLUS_UPPER_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"lower"
)
==
0
)
{
aiptek
->
newSetting
.
stylusButtonUpper
=
AIPTEK_STYLUS_LOWER_BUTTON
;
}
return
count
;
}
static
DEVICE_ATTR
(
stylus_upper
,
S_IRUGO
|
S_IWUGO
,
show_tabletStylusUpper
,
store_tabletStylusUpper
);
/***********************************************************************
* support routines for the 'stylus_lower' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletStylusLower
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
stylusButtonLower
)
{
case
AIPTEK_STYLUS_UPPER_BUTTON
:
{
s
=
"upper"
;
break
;
}
case
AIPTEK_STYLUS_LOWER_BUTTON
:
{
s
=
"lower"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletStylusLower
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"upper"
)
==
0
)
{
aiptek
->
newSetting
.
stylusButtonLower
=
AIPTEK_STYLUS_UPPER_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"lower"
)
==
0
)
{
aiptek
->
newSetting
.
stylusButtonLower
=
AIPTEK_STYLUS_LOWER_BUTTON
;
}
return
count
;
}
static
DEVICE_ATTR
(
stylus_lower
,
S_IRUGO
|
S_IWUGO
,
show_tabletStylusLower
,
store_tabletStylusLower
);
/***********************************************************************
* support routines for the 'mouse_left' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletMouseLeft
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
mouseButtonLeft
)
{
case
AIPTEK_MOUSE_LEFT_BUTTON
:
{
s
=
"left"
;
break
;
}
case
AIPTEK_MOUSE_MIDDLE_BUTTON
:
{
s
=
"middle"
;
break
;
}
case
AIPTEK_MOUSE_RIGHT_BUTTON
:
{
s
=
"right"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletMouseLeft
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"left"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonLeft
=
AIPTEK_MOUSE_LEFT_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"middle"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonLeft
=
AIPTEK_MOUSE_MIDDLE_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"right"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonLeft
=
AIPTEK_MOUSE_RIGHT_BUTTON
;
}
return
count
;
}
static
DEVICE_ATTR
(
mouse_left
,
S_IRUGO
|
S_IWUGO
,
show_tabletMouseLeft
,
store_tabletMouseLeft
);
/***********************************************************************
* support routines for the 'mouse_middle' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletMouseMiddle
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
mouseButtonMiddle
)
{
case
AIPTEK_MOUSE_LEFT_BUTTON
:
{
s
=
"left"
;
break
;
}
case
AIPTEK_MOUSE_MIDDLE_BUTTON
:
{
s
=
"middle"
;
break
;
}
case
AIPTEK_MOUSE_RIGHT_BUTTON
:
{
s
=
"right"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletMouseMiddle
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"left"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonMiddle
=
AIPTEK_MOUSE_LEFT_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"middle"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonMiddle
=
AIPTEK_MOUSE_MIDDLE_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"right"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonMiddle
=
AIPTEK_MOUSE_RIGHT_BUTTON
;
}
return
count
;
}
static
DEVICE_ATTR
(
mouse_middle
,
S_IRUGO
|
S_IWUGO
,
show_tabletMouseMiddle
,
store_tabletMouseMiddle
);
/***********************************************************************
* support routines for the 'mouse_right' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletMouseRight
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
char
*
s
;
if
(
aiptek
==
NULL
)
{
return
0
;
}
switch
(
aiptek
->
curSetting
.
mouseButtonRight
)
{
case
AIPTEK_MOUSE_LEFT_BUTTON
:
{
s
=
"left"
;
break
;
}
case
AIPTEK_MOUSE_MIDDLE_BUTTON
:
{
s
=
"middle"
;
break
;
}
case
AIPTEK_MOUSE_RIGHT_BUTTON
:
{
s
=
"right"
;
break
;
}
default:
{
s
=
"unknown"
;
break
;
}
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
s
);
}
static
ssize_t
store_tabletMouseRight
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
strcmp
(
buf
,
"left"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonRight
=
AIPTEK_MOUSE_LEFT_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"middle"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonRight
=
AIPTEK_MOUSE_MIDDLE_BUTTON
;
}
else
if
(
strcmp
(
buf
,
"right"
)
==
0
)
{
aiptek
->
newSetting
.
mouseButtonRight
=
AIPTEK_MOUSE_RIGHT_BUTTON
;
}
return
count
;
}
static
DEVICE_ATTR
(
mouse_right
,
S_IRUGO
|
S_IWUGO
,
show_tabletMouseRight
,
store_tabletMouseRight
);
/***********************************************************************
* support routines for the 'wheel' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletWheel
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
if
(
aiptek
->
curSetting
.
wheel
==
AIPTEK_WHEEL_DISABLE
)
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"disable
\n
"
);
}
else
{
return
snprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
aiptek
->
curSetting
.
wheel
);
}
}
static
ssize_t
store_tabletWheel
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
aiptek
->
newSetting
.
wheel
=
(
int
)
simple_strtol
(
buf
,
0
,
10
);
return
count
;
}
static
DEVICE_ATTR
(
wheel
,
S_IRUGO
|
S_IWUGO
,
show_tabletWheel
,
store_tabletWheel
);
/***********************************************************************
* support routines for the 'execute' file. Note that this file
* both displays current setting and allows for setting changing.
*/
static
ssize_t
show_tabletExecute
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
/* There is nothing useful to display, so a one-line manual
* is in order...
*/
return
snprintf
(
buf
,
PAGE_SIZE
,
"Write anything to this file to program your tablet.
\n
"
);
}
static
ssize_t
store_tabletExecute
(
struct
device
*
dev
,
const
char
*
buf
,
size_t
count
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
/* We do not care what you write to this file. Merely the action
* of writing to this file triggers a tablet reprogramming.
*/
memcpy
(
&
aiptek
->
curSetting
,
&
aiptek
->
newSetting
,
sizeof
(
struct
aiptek_settings
));
if
(
aiptek_program_tablet
(
aiptek
)
<
0
)
{
return
-
EIO
;
}
return
count
;
}
static
DEVICE_ATTR
(
execute
,
S_IRUGO
|
S_IWUGO
,
show_tabletExecute
,
store_tabletExecute
);
/***********************************************************************
* support routines for the 'odm_code' file. Note that this file
* only displays current setting.
*/
static
ssize_t
show_tabletODMCode
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%04x
\n
"
,
aiptek
->
features
.
odmCode
);
}
static
DEVICE_ATTR
(
odm_code
,
S_IRUGO
,
show_tabletODMCode
,
NULL
);
/***********************************************************************
* support routines for the 'model_code' file. Note that this file
* only displays current setting.
*/
static
ssize_t
show_tabletModelCode
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"0x%04x
\n
"
,
aiptek
->
features
.
modelCode
);
}
static
DEVICE_ATTR
(
model_code
,
S_IRUGO
,
show_tabletModelCode
,
NULL
);
/***********************************************************************
* support routines for the 'firmware_code' file. Note that this file
* only displays current setting.
*/
static
ssize_t
show_firmwareCode
(
struct
device
*
dev
,
char
*
buf
)
{
struct
aiptek
*
aiptek
=
dev_get_drvdata
(
dev
);
if
(
aiptek
==
NULL
)
{
return
0
;
}
return
snprintf
(
buf
,
PAGE_SIZE
,
"%04x
\n
"
,
aiptek
->
features
.
firmwareCode
);
}
static
DEVICE_ATTR
(
firmware_code
,
S_IRUGO
,
show_firmwareCode
,
NULL
);
/***********************************************************************
* This routine removes all existing sysfs files managed by this device
* driver.
*/
static
void
aiptek_delete_files
(
struct
device
*
dev
)
{
device_remove_file
(
dev
,
&
dev_attr_size
);
device_remove_file
(
dev
,
&
dev_attr_product_id
);
device_remove_file
(
dev
,
&
dev_attr_vendor_id
);
device_remove_file
(
dev
,
&
dev_attr_vendor
);
device_remove_file
(
dev
,
&
dev_attr_product
);
device_remove_file
(
dev
,
&
dev_attr_pointer_mode
);
device_remove_file
(
dev
,
&
dev_attr_coordinate_mode
);
device_remove_file
(
dev
,
&
dev_attr_tool_mode
);
device_remove_file
(
dev
,
&
dev_attr_xtilt
);
device_remove_file
(
dev
,
&
dev_attr_ytilt
);
device_remove_file
(
dev
,
&
dev_attr_jitter
);
device_remove_file
(
dev
,
&
dev_attr_delay
);
device_remove_file
(
dev
,
&
dev_attr_input_path
);
device_remove_file
(
dev
,
&
dev_attr_event_count
);
device_remove_file
(
dev
,
&
dev_attr_diagnostic
);
device_remove_file
(
dev
,
&
dev_attr_odm_code
);
device_remove_file
(
dev
,
&
dev_attr_model_code
);
device_remove_file
(
dev
,
&
dev_attr_firmware_code
);
device_remove_file
(
dev
,
&
dev_attr_stylus_lower
);
device_remove_file
(
dev
,
&
dev_attr_stylus_upper
);
device_remove_file
(
dev
,
&
dev_attr_mouse_left
);
device_remove_file
(
dev
,
&
dev_attr_mouse_middle
);
device_remove_file
(
dev
,
&
dev_attr_mouse_right
);
device_remove_file
(
dev
,
&
dev_attr_wheel
);
device_remove_file
(
dev
,
&
dev_attr_execute
);
}
/***********************************************************************
* This routine creates the sysfs files managed by this device
* driver.
*/
static
int
aiptek_add_files
(
struct
device
*
dev
)
{
int
ret
;
if
((
ret
=
device_create_file
(
dev
,
&
dev_attr_size
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_product_id
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_vendor_id
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_vendor
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_product
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_pointer_mode
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_coordinate_mode
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_tool_mode
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_xtilt
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_ytilt
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_jitter
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_delay
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_input_path
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_event_count
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_diagnostic
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_odm_code
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_model_code
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_firmware_code
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_stylus_lower
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_stylus_upper
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_mouse_left
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_mouse_middle
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_mouse_right
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_wheel
))
||
(
ret
=
device_create_file
(
dev
,
&
dev_attr_execute
)))
{
err
(
"aiptek: killing own sysfs device files
\n
"
);
aiptek_delete_files
(
dev
);
}
return
ret
;
}
/***********************************************************************
* This routine is called when a tablet has been identified. It basically
* sets up the tablet and the driver's internal structures.
*/
static
int
aiptek_probe
(
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
)
{
struct
usb_device
*
usbdev
=
interface_to_usbdev
(
intf
);
struct
usb_endpoint_descriptor
*
endpoint
;
struct
aiptek
*
aiptek
;
struct
input_dev
*
inputdev
;
struct
input_handle
*
inputhandle
;
struct
list_head
*
node
,
*
next
;
char
path
[
64
+
1
];
int
i
;
int
speeds
[]
=
{
0
,
AIPTEK_PROGRAMMABLE_DELAY_50
,
AIPTEK_PROGRAMMABLE_DELAY_400
,
AIPTEK_PROGRAMMABLE_DELAY_25
,
AIPTEK_PROGRAMMABLE_DELAY_100
,
AIPTEK_PROGRAMMABLE_DELAY_200
,
AIPTEK_PROGRAMMABLE_DELAY_300
}
;
/* programmableDelay is where the command-line specified
* delay is kept. We make it the first element of speeds[],
* so therefore, your override speed is tried first, then the
* remainder. Note that the default value of 400ms will be tried
* if you do not specify any command line parameter.
*/
speeds
[
0
]
=
programmableDelay
;
if
((
aiptek
=
kmalloc
(
sizeof
(
struct
aiptek
),
GFP_KERNEL
))
==
NULL
)
{
return
-
ENOMEM
;
}
memset
(
aiptek
,
0
,
sizeof
(
struct
aiptek
));
aiptek
->
data
=
usb_buffer_alloc
(
usbdev
,
AIPTEK_PACKET_LENGTH
,
SLAB_ATOMIC
,
&
aiptek
->
data_dma
);
if
(
aiptek
->
data
==
NULL
)
{
kfree
(
aiptek
);
return
-
ENOMEM
;
}
aiptek
->
urb
=
usb_alloc_urb
(
0
,
GFP_KERNEL
);
if
(
aiptek
->
urb
==
NULL
)
{
usb_buffer_free
(
usbdev
,
AIPTEK_PACKET_LENGTH
,
aiptek
->
data
,
aiptek
->
data_dma
);
kfree
(
aiptek
);
return
-
ENOMEM
;
}
/* Set up the curSettings struct. Said struct contains the current
* programmable parameters. The newSetting struct contains changes
* the user makes to the settings via the sysfs interface. Those
* changes are not "committed" to curSettings until the user
* writes to the sysfs/.../execute file.
*/
aiptek
->
curSetting
.
pointerMode
=
AIPTEK_POINTER_EITHER_MODE
;
aiptek
->
curSetting
.
coordinateMode
=
AIPTEK_COORDINATE_ABSOLUTE_MODE
;
aiptek
->
curSetting
.
toolMode
=
AIPTEK_TOOL_BUTTON_PEN_MODE
;
aiptek
->
curSetting
.
xTilt
=
AIPTEK_TILT_DISABLE
;
aiptek
->
curSetting
.
yTilt
=
AIPTEK_TILT_DISABLE
;
aiptek
->
curSetting
.
mouseButtonLeft
=
AIPTEK_MOUSE_LEFT_BUTTON
;
aiptek
->
curSetting
.
mouseButtonMiddle
=
AIPTEK_MOUSE_MIDDLE_BUTTON
;
aiptek
->
curSetting
.
mouseButtonRight
=
AIPTEK_MOUSE_RIGHT_BUTTON
;
aiptek
->
curSetting
.
stylusButtonUpper
=
AIPTEK_STYLUS_UPPER_BUTTON
;
aiptek
->
curSetting
.
stylusButtonLower
=
AIPTEK_STYLUS_LOWER_BUTTON
;
aiptek
->
curSetting
.
jitterDelay
=
jitterDelay
;
aiptek
->
curSetting
.
programmableDelay
=
programmableDelay
;
/* Both structs should have equivalent settings
*/
memcpy
(
&
aiptek
->
newSetting
,
&
aiptek
->
curSetting
,
sizeof
(
struct
aiptek_settings
));
/* Now program the capacities of the tablet, in terms of being
* an input device.
*/
aiptek
->
inputdev
.
evbit
[
0
]
|=
BIT
(
EV_KEY
)
|
BIT
(
EV_ABS
)
|
BIT
(
EV_REL
)
|
BIT
(
EV_MSC
);
aiptek
->
inputdev
.
absbit
[
0
]
|=
(
BIT
(
ABS_X
)
|
BIT
(
ABS_Y
)
|
BIT
(
ABS_PRESSURE
)
|
BIT
(
ABS_TILT_X
)
|
BIT
(
ABS_TILT_Y
)
|
BIT
(
ABS_WHEEL
)
|
BIT
(
ABS_MISC
));
aiptek
->
inputdev
.
relbit
[
0
]
|=
(
BIT
(
REL_X
)
|
BIT
(
REL_Y
)
|
BIT
(
REL_WHEEL
)
|
BIT
(
REL_MISC
));
aiptek
->
inputdev
.
keybit
[
LONG
(
BTN_LEFT
)]
|=
(
BIT
(
BTN_LEFT
)
|
BIT
(
BTN_RIGHT
)
|
BIT
(
BTN_MIDDLE
));
aiptek
->
inputdev
.
keybit
[
LONG
(
BTN_DIGI
)]
|=
(
BIT
(
BTN_TOOL_PEN
)
|
BIT
(
BTN_TOOL_RUBBER
)
|
BIT
(
BTN_TOOL_PENCIL
)
|
BIT
(
BTN_TOOL_AIRBRUSH
)
|
BIT
(
BTN_TOOL_BRUSH
)
|
BIT
(
BTN_TOOL_MOUSE
)
|
BIT
(
BTN_TOOL_LENS
)
|
BIT
(
BTN_TOUCH
)
|
BIT
(
BTN_STYLUS
)
|
BIT
(
BTN_STYLUS2
));
aiptek
->
inputdev
.
mscbit
[
0
]
=
BIT
(
MSC_SERIAL
);
/* Programming the tablet macro keys needs to be done with a for loop
* as the keycodes are discontiguous.
*/
for
(
i
=
0
;
i
<
sizeof
(
macroKeyEvents
)
/
sizeof
(
macroKeyEvents
[
0
]);
++
i
)
{
set_bit
(
macroKeyEvents
[
i
],
aiptek
->
inputdev
.
keybit
);
}
/* Set up client data, pointers to open and close routines
* for the input device.
*/
aiptek
->
inputdev
.
private
=
aiptek
;
aiptek
->
inputdev
.
open
=
aiptek_open
;
aiptek
->
inputdev
.
close
=
aiptek_close
;
/* Determine the usb devices' physical path.
* Asketh not why we always pretend we're using "../input0",
* but I suspect this will have to be refactored one
* day if a single USB device can be a keyboard & a mouse
* & a tablet, and the inputX number actually will tell
* us something...
*/
if
(
usb_make_path
(
usbdev
,
path
,
64
)
>
0
)
{
sprintf
(
aiptek
->
features
.
usbPath
,
"%s/input0"
,
path
);
}
/* Program the input device coordinate capacities. We do not yet
* know what maximum X, Y, and Z values are, so we're putting fake
* values in. Later, we'll ask the tablet to put in the correct
* values.
*/
aiptek
->
inputdev
.
absmin
[
ABS_X
]
=
0
;
aiptek
->
inputdev
.
absmax
[
ABS_X
]
=
2999
;
aiptek
->
inputdev
.
absmin
[
ABS_Y
]
=
0
;
aiptek
->
inputdev
.
absmax
[
ABS_Y
]
=
2249
;
aiptek
->
inputdev
.
absmin
[
ABS_PRESSURE
]
=
0
;
aiptek
->
inputdev
.
absmax
[
ABS_PRESSURE
]
=
511
;
aiptek
->
inputdev
.
absmin
[
ABS_TILT_X
]
=
AIPTEK_TILT_MIN
;
aiptek
->
inputdev
.
absmax
[
ABS_TILT_X
]
=
AIPTEK_TILT_MAX
;
aiptek
->
inputdev
.
absmin
[
ABS_TILT_Y
]
=
AIPTEK_TILT_MIN
;
aiptek
->
inputdev
.
absmax
[
ABS_TILT_Y
]
=
AIPTEK_TILT_MAX
;
aiptek
->
inputdev
.
absmin
[
ABS_WHEEL
]
=
AIPTEK_WHEEL_MIN
;
aiptek
->
inputdev
.
absmax
[
ABS_WHEEL
]
=
AIPTEK_WHEEL_MAX
-
1
;
aiptek
->
inputdev
.
absfuzz
[
ABS_X
]
=
0
;
aiptek
->
inputdev
.
absfuzz
[
ABS_Y
]
=
0
;
aiptek
->
inputdev
.
absfuzz
[
ABS_PRESSURE
]
=
0
;
aiptek
->
inputdev
.
absfuzz
[
ABS_TILT_X
]
=
0
;
aiptek
->
inputdev
.
absfuzz
[
ABS_TILT_Y
]
=
0
;
aiptek
->
inputdev
.
absfuzz
[
ABS_WHEEL
]
=
0
;
aiptek
->
inputdev
.
absflat
[
ABS_X
]
=
0
;
aiptek
->
inputdev
.
absflat
[
ABS_Y
]
=
0
;
aiptek
->
inputdev
.
absflat
[
ABS_PRESSURE
]
=
0
;
aiptek
->
inputdev
.
absflat
[
ABS_TILT_X
]
=
0
;
aiptek
->
inputdev
.
absflat
[
ABS_TILT_Y
]
=
0
;
aiptek
->
inputdev
.
absflat
[
ABS_WHEEL
]
=
0
;
aiptek
->
inputdev
.
name
=
"Aiptek"
;
aiptek
->
inputdev
.
phys
=
aiptek
->
features
.
usbPath
;
aiptek
->
inputdev
.
id
.
bustype
=
BUS_USB
;
aiptek
->
inputdev
.
id
.
vendor
=
usbdev
->
descriptor
.
idVendor
;
aiptek
->
inputdev
.
id
.
product
=
usbdev
->
descriptor
.
idProduct
;
aiptek
->
inputdev
.
id
.
version
=
usbdev
->
descriptor
.
bcdDevice
;
aiptek
->
usbdev
=
usbdev
;
aiptek
->
ifnum
=
intf
->
altsetting
[
0
].
desc
.
bInterfaceNumber
;
aiptek
->
inDelay
=
0
;
aiptek
->
endDelay
=
0
;
aiptek
->
previousJitterable
=
0
;
endpoint
=
&
intf
->
altsetting
[
0
].
endpoint
[
0
].
desc
;
/* Go set up our URB, which is called when the tablet receives
* input.
*/
usb_fill_int_urb
(
aiptek
->
urb
,
aiptek
->
usbdev
,
usb_rcvintpipe
(
aiptek
->
usbdev
,
endpoint
->
bEndpointAddress
),
aiptek
->
data
,
8
,
aiptek_irq
,
aiptek
,
endpoint
->
bInterval
);
aiptek
->
urb
->
transfer_dma
=
aiptek
->
data_dma
;
aiptek
->
urb
->
transfer_flags
|=
URB_NO_TRANSFER_DMA_MAP
;
/* Register the tablet as an Input Device
*/
input_register_device
(
&
aiptek
->
inputdev
);
/* Go and decode the USB representation of the tablet's manufacturer
* name and product name. They only change once every hotplug event,
* which is why we put it here instead of in the sysfs interface.
*/
usb_string
(
usbdev
,
usbdev
->
descriptor
.
iManufacturer
,
aiptek
->
features
.
manuName
,
sizeof
(
aiptek
->
features
.
manuName
));
usb_string
(
usbdev
,
usbdev
->
descriptor
.
iProduct
,
aiptek
->
features
.
prodName
,
sizeof
(
aiptek
->
features
.
prodName
));
/* We now will look for the evdev device which is mapped to
* the tablet. The partial name is kept in the link list of
* input_handles associated with this input device.
* What identifies an evdev input_handler is that it begins
* with 'event', continues with a digit, and that in turn
* is mapped to /{devfs}/input/eventN.
*/
inputdev
=
&
aiptek
->
inputdev
;
list_for_each_safe
(
node
,
next
,
&
inputdev
->
h_list
)
{
inputhandle
=
to_handle
(
node
);
if
(
strncmp
(
inputhandle
->
name
,
"event"
,
5
)
==
0
)
{
strcpy
(
aiptek
->
features
.
inputPath
,
inputhandle
->
name
);
break
;
}
}
info
(
"input: Aiptek on %s (%s)
\n
"
,
path
,
aiptek
->
features
.
inputPath
);
/* Program the tablet. This sets the tablet up in the mode
* specified in newSetting, and also queries the tablet's
* physical capacities.
*
* Sanity check: if a tablet doesn't like the slow programmatic
* delay, we often get sizes of 0x0. Let's use that as an indicator
* to try faster delays, up to 25 ms. If that logic fails, well, you'll
* have to explain to us how your tablet thinks it's 0x0, and yet that's
* not an error :-)
*/
for
(
i
=
0
;
i
<
sizeof
(
speeds
)
/
sizeof
(
speeds
[
0
]);
++
i
)
{
aiptek
->
curSetting
.
programmableDelay
=
speeds
[
i
];
(
void
)
aiptek_program_tablet
(
aiptek
);
if
(
aiptek
->
inputdev
.
absmax
[
ABS_X
]
>
0
)
{
info
(
"input: Aiptek using %d ms programming speed
\n
"
,
aiptek
->
curSetting
.
programmableDelay
);
break
;
}
}
/* Associate this driver's struct with the usb interface.
*/
usb_set_intfdata
(
intf
,
aiptek
);
/* Set up the sysfs files
*/
aiptek_add_files
(
&
intf
->
dev
);
/* Make sure the evdev module is loaded. Assuming evdev IS a module :-)
*/
if
(
request_module
(
"evdev"
)
!=
0
)
{
info
(
"aiptek: error loading 'evdev' module"
);
}
return
0
;
}
/* Forward declaration */
static
void
aiptek_disconnect
(
struct
usb_interface
*
intf
);
static
struct
usb_driver
aiptek_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"aiptek"
,
.
probe
=
aiptek_probe
,
.
disconnect
=
aiptek_disconnect
,
.
id_table
=
aiptek_ids
,
}
;
/***********************************************************************
* Deal with tablet disconnecting from the system.
*/
static
void
aiptek_disconnect
(
struct
usb_interface
*
intf
)
{
struct
aiptek
*
aiptek
=
usb_get_intfdata
(
intf
);
/* Disassociate driver's struct with usb interface
*/
usb_set_intfdata
(
intf
,
NULL
);
if
(
aiptek
!=
NULL
)
{
/* Free & unhook everything from the system.
*/
usb_unlink_urb
(
aiptek
->
urb
);
input_unregister_device
(
&
aiptek
->
inputdev
);
aiptek_delete_files
(
&
intf
->
dev
);
usb_free_urb
(
aiptek
->
urb
);
usb_buffer_free
(
interface_to_usbdev
(
intf
),
AIPTEK_PACKET_LENGTH
,
aiptek
->
data
,
aiptek
->
data_dma
);
kfree
(
aiptek
);
aiptek
=
NULL
;
}
}
static
int
__init
aiptek_init
(
void
)
{
int
result
=
usb_register
(
&
aiptek_driver
);
if
(
result
==
0
)
{
info
(
DRIVER_VERSION
": "
DRIVER_AUTHOR
);
info
(
DRIVER_DESC
);
}
return
result
;
}
static
void
__exit
aiptek_exit
(
void
)
{
usb_deregister
(
&
aiptek_driver
);
}
MODULE_AUTHOR
(
DRIVER_AUTHOR
);
MODULE_DESCRIPTION
(
DRIVER_DESC
);
MODULE_LICENSE
(
"GPL"
);
MODULE_PARM
(
programmableDelay
,
"i"
);
MODULE_PARM_DESC
(
programmableDelay
,
"delay used during tablet programming"
);
MODULE_PARM
(
jitterDelay
,
"i"
);
MODULE_PARM_DESC
(
jitterDelay
,
"stylus/mouse settlement delay"
);
module_init
(
aiptek_init
);
module_exit
(
aiptek_exit
);
drivers/usb/input/hid-core.c
View file @
6aecb90d
...
...
@@ -1359,7 +1359,13 @@ void hid_init_reports(struct hid_device *hid)
#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
#define USB_VENDOR_ID_AIPTEK 0x08ca
#define USB_DEVICE_ID_AIPTEK_6000 0x0020
#define USB_DEVICE_ID_AIPTEK_01 0x0001
#define USB_DEVICE_ID_AIPTEK_10 0x0010
#define USB_DEVICE_ID_AIPTEK_20 0x0020
#define USB_DEVICE_ID_AIPTEK_21 0x0021
#define USB_DEVICE_ID_AIPTEK_22 0x0022
#define USB_DEVICE_ID_AIPTEK_23 0x0023
#define USB_DEVICE_ID_AIPTEK_24 0x0024
#define USB_VENDOR_ID_GRIFFIN 0x077d
#define USB_DEVICE_ID_POWERMATE 0x0410
...
...
@@ -1428,7 +1434,13 @@ static struct hid_blacklist {
unsigned
quirks
;
}
hid_blacklist
[]
=
{
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_6000
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_01
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_10
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_20
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_21
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_22
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_23
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_AIPTEK
,
USB_DEVICE_ID_AIPTEK_24
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_BERKSHIRE
,
USB_DEVICE_ID_BERKSHIRE_PCWD
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_ESSENTIAL_REALITY
,
USB_DEVICE_ID_ESSENTIAL_REALITY_P5
,
HID_QUIRK_IGNORE
},
{
USB_VENDOR_ID_KBGEAR
,
USB_DEVICE_ID_KBGEAR_JAMSTUDIO
,
HID_QUIRK_IGNORE
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment