forked from Minki/linux
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (58 commits) Input: wacom_w8001 - support pen or touch only devices Input: wacom_w8001 - use __set_bit to set keybits Input: bu21013_ts - fix misuse of logical operation in place of bitop Input: i8042 - add Acer Aspire 5100 to the Dritek list Input: wacom - add support for digitizer in Lenovo W700 Input: psmouse - disable the synaptics extension on OLPC machines Input: psmouse - fix up Synaptics comment Input: synaptics - ignore bogus mt packet Input: synaptics - add multi-finger and semi-mt support Input: synaptics - report clickpad property input: mt: Document interface updates Input: fix double equality sign in uevent Input: introduce device properties hid: egalax: Add support for Wetab (726b) Input: include MT library as source for kerneldoc MAINTAINERS: Update input-mt entry hid: egalax: Add support for Samsung NB30 netbook hid: egalax: Document the new devices in Kconfig hid: egalax: Add support for Wetab hid: egalax: Convert to MT slots ... Fixed up trivial conflict in drivers/input/keyboard/Kconfig
This commit is contained in:
commit
31b6ca0af7
@ -303,6 +303,10 @@ X!Idrivers/video/console/fonts.c
|
||||
!Edrivers/input/input.c
|
||||
!Edrivers/input/ff-core.c
|
||||
!Edrivers/input/ff-memless.c
|
||||
</sect1>
|
||||
<sect1><title>Multitouch Library</title>
|
||||
!Iinclude/linux/input/mt.h
|
||||
!Edrivers/input/input-mt.c
|
||||
</sect1>
|
||||
<sect1><title>Polled input devices</title>
|
||||
!Iinclude/linux/input-polldev.h
|
||||
|
115
Documentation/input/cma3000_d0x.txt
Normal file
115
Documentation/input/cma3000_d0x.txt
Normal file
@ -0,0 +1,115 @@
|
||||
Kernel driver for CMA3000-D0x
|
||||
============================
|
||||
|
||||
Supported chips:
|
||||
* VTI CMA3000-D0x
|
||||
Datasheet:
|
||||
CMA3000-D0X Product Family Specification 8281000A.02.pdf
|
||||
<http://www.vti.fi/en/>
|
||||
|
||||
Author: Hemanth V <hemanthv@ti.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
CMA3000 Tri-axis accelerometer supports Motion detect, Measurement and
|
||||
Free fall modes.
|
||||
|
||||
Motion Detect Mode: Its the low power mode where interrupts are generated only
|
||||
when motion exceeds the defined thresholds.
|
||||
|
||||
Measurement Mode: This mode is used to read the acceleration data on X,Y,Z
|
||||
axis and supports 400, 100, 40 Hz sample frequency.
|
||||
|
||||
Free fall Mode: This mode is intended to save system resources.
|
||||
|
||||
Threshold values: Chip supports defining threshold values for above modes
|
||||
which includes time and g value. Refer product specifications for more details.
|
||||
|
||||
CMA3000 chip supports mutually exclusive I2C and SPI interfaces for
|
||||
communication, currently the driver supports I2C based communication only.
|
||||
Initial configuration for bus mode is set in non volatile memory and can later
|
||||
be modified through bus interface command.
|
||||
|
||||
Driver reports acceleration data through input subsystem. It generates ABS_MISC
|
||||
event with value 1 when free fall is detected.
|
||||
|
||||
Platform data need to be configured for initial default values.
|
||||
|
||||
Platform Data
|
||||
-------------
|
||||
fuzz_x: Noise on X Axis
|
||||
|
||||
fuzz_y: Noise on Y Axis
|
||||
|
||||
fuzz_z: Noise on Z Axis
|
||||
|
||||
g_range: G range in milli g i.e 2000 or 8000
|
||||
|
||||
mode: Default Operating mode
|
||||
|
||||
mdthr: Motion detect g range threshold value
|
||||
|
||||
mdfftmr: Motion detect and free fall time threshold value
|
||||
|
||||
ffthr: Free fall g range threshold value
|
||||
|
||||
Input Interface
|
||||
--------------
|
||||
Input driver version is 1.0.0
|
||||
Input device ID: bus 0x18 vendor 0x0 product 0x0 version 0x0
|
||||
Input device name: "cma3000-accelerometer"
|
||||
Supported events:
|
||||
Event type 0 (Sync)
|
||||
Event type 3 (Absolute)
|
||||
Event code 0 (X)
|
||||
Value 47
|
||||
Min -8000
|
||||
Max 8000
|
||||
Fuzz 200
|
||||
Event code 1 (Y)
|
||||
Value -28
|
||||
Min -8000
|
||||
Max 8000
|
||||
Fuzz 200
|
||||
Event code 2 (Z)
|
||||
Value 905
|
||||
Min -8000
|
||||
Max 8000
|
||||
Fuzz 200
|
||||
Event code 40 (Misc)
|
||||
Value 0
|
||||
Min 0
|
||||
Max 1
|
||||
Event type 4 (Misc)
|
||||
|
||||
|
||||
Register/Platform parameters Description
|
||||
----------------------------------------
|
||||
|
||||
mode:
|
||||
0: power down mode
|
||||
1: 100 Hz Measurement mode
|
||||
2: 400 Hz Measurement mode
|
||||
3: 40 Hz Measurement mode
|
||||
4: Motion Detect mode (default)
|
||||
5: 100 Hz Free fall mode
|
||||
6: 40 Hz Free fall mode
|
||||
7: Power off mode
|
||||
|
||||
grange:
|
||||
2000: 2000 mg or 2G Range
|
||||
8000: 8000 mg or 8G Range
|
||||
|
||||
mdthr:
|
||||
X: X * 71mg (8G Range)
|
||||
X: X * 18mg (2G Range)
|
||||
|
||||
mdfftmr:
|
||||
X: (X & 0x70) * 100 ms (MDTMR)
|
||||
(X & 0x0F) * 2.5 ms (FFTMR 400 Hz)
|
||||
(X & 0x0F) * 10 ms (FFTMR 100 Hz)
|
||||
|
||||
ffthr:
|
||||
X: (X >> 2) * 18mg (2G Range)
|
||||
X: (X & 0x0F) * 71 mg (8G Range)
|
@ -1,6 +1,6 @@
|
||||
Multi-touch (MT) Protocol
|
||||
-------------------------
|
||||
Copyright (C) 2009 Henrik Rydberg <rydberg@euromail.se>
|
||||
Copyright (C) 2009-2010 Henrik Rydberg <rydberg@euromail.se>
|
||||
|
||||
|
||||
Introduction
|
||||
@ -161,19 +161,24 @@ against the glass. The inner region will increase, and in general, the
|
||||
ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than
|
||||
unity, is related to the contact pressure. For pressure-based devices,
|
||||
ABS_MT_PRESSURE may be used to provide the pressure on the contact area
|
||||
instead.
|
||||
instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to
|
||||
indicate the distance between the contact and the surface.
|
||||
|
||||
In addition to the MAJOR parameters, the oval shape of the contact can be
|
||||
described by adding the MINOR parameters, such that MAJOR and MINOR are the
|
||||
major and minor axis of an ellipse. Finally, the orientation of the oval
|
||||
shape can be describe with the ORIENTATION parameter.
|
||||
|
||||
For type A devices, further specification of the touch shape is possible
|
||||
via ABS_MT_BLOB_ID.
|
||||
|
||||
The ABS_MT_TOOL_TYPE may be used to specify whether the touching tool is a
|
||||
contact or a pen or something else. Devices with more granular information
|
||||
may specify general shapes as blobs, i.e., as a sequence of rectangular
|
||||
shapes grouped together by an ABS_MT_BLOB_ID. Finally, for the few devices
|
||||
that currently support it, the ABS_MT_TRACKING_ID event may be used to
|
||||
report contact tracking from hardware [5].
|
||||
finger or a pen or something else. Finally, the ABS_MT_TRACKING_ID event
|
||||
may be used to track identified contacts over time [5].
|
||||
|
||||
In the type B protocol, ABS_MT_TOOL_TYPE and ABS_MT_TRACKING_ID are
|
||||
implicitly handled by input core; drivers should instead call
|
||||
input_mt_report_slot_state().
|
||||
|
||||
|
||||
Event Semantics
|
||||
@ -213,6 +218,12 @@ The pressure, in arbitrary units, on the contact area. May be used instead
|
||||
of TOUCH and WIDTH for pressure-based devices or any device with a spatial
|
||||
signal intensity distribution.
|
||||
|
||||
ABS_MT_DISTANCE
|
||||
|
||||
The distance, in surface units, between the contact and the surface. Zero
|
||||
distance means the contact is touching the surface. A positive number means
|
||||
the contact is hovering above the surface.
|
||||
|
||||
ABS_MT_ORIENTATION
|
||||
|
||||
The orientation of the ellipse. The value should describe a signed quarter
|
||||
@ -240,21 +251,24 @@ ABS_MT_TOOL_TYPE
|
||||
The type of approaching tool. A lot of kernel drivers cannot distinguish
|
||||
between different tool types, such as a finger or a pen. In such cases, the
|
||||
event should be omitted. The protocol currently supports MT_TOOL_FINGER and
|
||||
MT_TOOL_PEN [2].
|
||||
MT_TOOL_PEN [2]. For type B devices, this event is handled by input core;
|
||||
drivers should instead use input_mt_report_slot_state().
|
||||
|
||||
ABS_MT_BLOB_ID
|
||||
|
||||
The BLOB_ID groups several packets together into one arbitrarily shaped
|
||||
contact. This is a low-level anonymous grouping for type A devices, and
|
||||
contact. The sequence of points forms a polygon which defines the shape of
|
||||
the contact. This is a low-level anonymous grouping for type A devices, and
|
||||
should not be confused with the high-level trackingID [5]. Most type A
|
||||
devices do not have blob capability, so drivers can safely omit this event.
|
||||
|
||||
ABS_MT_TRACKING_ID
|
||||
|
||||
The TRACKING_ID identifies an initiated contact throughout its life cycle
|
||||
[5]. This event is mandatory for type B devices. The value range of the
|
||||
TRACKING_ID should be large enough to ensure unique identification of a
|
||||
contact maintained over an extended period of time.
|
||||
[5]. The value range of the TRACKING_ID should be large enough to ensure
|
||||
unique identification of a contact maintained over an extended period of
|
||||
time. For type B devices, this event is handled by input core; drivers
|
||||
should instead use input_mt_report_slot_state().
|
||||
|
||||
|
||||
Event Computation
|
||||
@ -301,18 +315,19 @@ and with ORIENTATION, one can detect twisting of fingers.
|
||||
Notes
|
||||
-----
|
||||
|
||||
In order to stay compatible with existing applications, the data
|
||||
reported in a finger packet must not be recognized as single-touch
|
||||
events. In addition, all finger data must bypass input filtering,
|
||||
since subsequent events of the same type refer to different fingers.
|
||||
In order to stay compatible with existing applications, the data reported
|
||||
in a finger packet must not be recognized as single-touch events.
|
||||
|
||||
The first kernel driver to utilize the MT protocol is the bcm5974 driver,
|
||||
where examples can be found.
|
||||
For type A devices, all finger data bypasses input filtering, since
|
||||
subsequent events of the same type refer to different fingers.
|
||||
|
||||
For example usage of the type A protocol, see the bcm5974 driver. For
|
||||
example usage of the type B protocol, see the hid-egalax driver.
|
||||
|
||||
[1] With the extension ABS_MT_APPROACH_X and ABS_MT_APPROACH_Y, the
|
||||
difference between the contact position and the approaching tool position
|
||||
could be used to derive tilt.
|
||||
[2] The list can of course be extended.
|
||||
[3] Multitouch X driver project: http://bitmath.org/code/multitouch/.
|
||||
[3] The mtdev project: http://bitmath.org/code/mtdev/.
|
||||
[4] See the section on event computation.
|
||||
[5] See the section on finger tracking.
|
||||
|
@ -3065,8 +3065,10 @@ F: drivers/input/
|
||||
INPUT MULTITOUCH (MT) PROTOCOL
|
||||
M: Henrik Rydberg <rydberg@euromail.se>
|
||||
L: linux-input@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rydberg/input-mt.git
|
||||
S: Maintained
|
||||
F: Documentation/input/multi-touch-protocol.txt
|
||||
F: drivers/input/input-mt.c
|
||||
K: \b(ABS|SYN)_MT_
|
||||
|
||||
INTEL IDLE DRIVER
|
||||
|
141
arch/arm/plat-spear/include/plat/keyboard.h
Normal file
141
arch/arm/plat-spear/include/plat/keyboard.h
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2010 ST Microelectronics
|
||||
* Rajeev Kumar<rajeev-dlh.kumar@st.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __PLAT_KEYBOARD_H
|
||||
#define __PLAT_KEYBOARD_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DECLARE_KEYMAP(_name) \
|
||||
int _name[] = { \
|
||||
KEY(0, 0, KEY_ESC), \
|
||||
KEY(0, 1, KEY_1), \
|
||||
KEY(0, 2, KEY_2), \
|
||||
KEY(0, 3, KEY_3), \
|
||||
KEY(0, 4, KEY_4), \
|
||||
KEY(0, 5, KEY_5), \
|
||||
KEY(0, 6, KEY_6), \
|
||||
KEY(0, 7, KEY_7), \
|
||||
KEY(0, 8, KEY_8), \
|
||||
KEY(1, 0, KEY_9), \
|
||||
KEY(1, 1, KEY_MINUS), \
|
||||
KEY(1, 2, KEY_EQUAL), \
|
||||
KEY(1, 3, KEY_BACKSPACE), \
|
||||
KEY(1, 4, KEY_TAB), \
|
||||
KEY(1, 5, KEY_Q), \
|
||||
KEY(1, 6, KEY_W), \
|
||||
KEY(1, 7, KEY_E), \
|
||||
KEY(1, 8, KEY_R), \
|
||||
KEY(2, 0, KEY_T), \
|
||||
KEY(2, 1, KEY_Y), \
|
||||
KEY(2, 2, KEY_U), \
|
||||
KEY(2, 3, KEY_I), \
|
||||
KEY(2, 4, KEY_O), \
|
||||
KEY(2, 5, KEY_P), \
|
||||
KEY(2, 6, KEY_LEFTBRACE), \
|
||||
KEY(2, 7, KEY_RIGHTBRACE), \
|
||||
KEY(2, 8, KEY_ENTER), \
|
||||
KEY(3, 0, KEY_LEFTCTRL), \
|
||||
KEY(3, 1, KEY_A), \
|
||||
KEY(3, 2, KEY_S), \
|
||||
KEY(3, 3, KEY_D), \
|
||||
KEY(3, 4, KEY_F), \
|
||||
KEY(3, 5, KEY_G), \
|
||||
KEY(3, 6, KEY_H), \
|
||||
KEY(3, 7, KEY_J), \
|
||||
KEY(3, 8, KEY_K), \
|
||||
KEY(4, 0, KEY_L), \
|
||||
KEY(4, 1, KEY_SEMICOLON), \
|
||||
KEY(4, 2, KEY_APOSTROPHE), \
|
||||
KEY(4, 3, KEY_GRAVE), \
|
||||
KEY(4, 4, KEY_LEFTSHIFT), \
|
||||
KEY(4, 5, KEY_BACKSLASH), \
|
||||
KEY(4, 6, KEY_Z), \
|
||||
KEY(4, 7, KEY_X), \
|
||||
KEY(4, 8, KEY_C), \
|
||||
KEY(4, 0, KEY_L), \
|
||||
KEY(4, 1, KEY_SEMICOLON), \
|
||||
KEY(4, 2, KEY_APOSTROPHE), \
|
||||
KEY(4, 3, KEY_GRAVE), \
|
||||
KEY(4, 4, KEY_LEFTSHIFT), \
|
||||
KEY(4, 5, KEY_BACKSLASH), \
|
||||
KEY(4, 6, KEY_Z), \
|
||||
KEY(4, 7, KEY_X), \
|
||||
KEY(4, 8, KEY_C), \
|
||||
KEY(4, 0, KEY_L), \
|
||||
KEY(4, 1, KEY_SEMICOLON), \
|
||||
KEY(4, 2, KEY_APOSTROPHE), \
|
||||
KEY(4, 3, KEY_GRAVE), \
|
||||
KEY(4, 4, KEY_LEFTSHIFT), \
|
||||
KEY(4, 5, KEY_BACKSLASH), \
|
||||
KEY(4, 6, KEY_Z), \
|
||||
KEY(4, 7, KEY_X), \
|
||||
KEY(4, 8, KEY_C), \
|
||||
KEY(5, 0, KEY_V), \
|
||||
KEY(5, 1, KEY_B), \
|
||||
KEY(5, 2, KEY_N), \
|
||||
KEY(5, 3, KEY_M), \
|
||||
KEY(5, 4, KEY_COMMA), \
|
||||
KEY(5, 5, KEY_DOT), \
|
||||
KEY(5, 6, KEY_SLASH), \
|
||||
KEY(5, 7, KEY_RIGHTSHIFT), \
|
||||
KEY(5, 8, KEY_KPASTERISK), \
|
||||
KEY(6, 0, KEY_LEFTALT), \
|
||||
KEY(6, 1, KEY_SPACE), \
|
||||
KEY(6, 2, KEY_CAPSLOCK), \
|
||||
KEY(6, 3, KEY_F1), \
|
||||
KEY(6, 4, KEY_F2), \
|
||||
KEY(6, 5, KEY_F3), \
|
||||
KEY(6, 6, KEY_F4), \
|
||||
KEY(6, 7, KEY_F5), \
|
||||
KEY(6, 8, KEY_F6), \
|
||||
KEY(7, 0, KEY_F7), \
|
||||
KEY(7, 1, KEY_F8), \
|
||||
KEY(7, 2, KEY_F9), \
|
||||
KEY(7, 3, KEY_F10), \
|
||||
KEY(7, 4, KEY_NUMLOCK), \
|
||||
KEY(7, 5, KEY_SCROLLLOCK), \
|
||||
KEY(7, 6, KEY_KP7), \
|
||||
KEY(7, 7, KEY_KP8), \
|
||||
KEY(7, 8, KEY_KP9), \
|
||||
KEY(8, 0, KEY_KPMINUS), \
|
||||
KEY(8, 1, KEY_KP4), \
|
||||
KEY(8, 2, KEY_KP5), \
|
||||
KEY(8, 3, KEY_KP6), \
|
||||
KEY(8, 4, KEY_KPPLUS), \
|
||||
KEY(8, 5, KEY_KP1), \
|
||||
KEY(8, 6, KEY_KP2), \
|
||||
KEY(8, 7, KEY_KP3), \
|
||||
KEY(8, 8, KEY_KP0), \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct kbd_platform_data - spear keyboard platform data
|
||||
* keymap: pointer to keymap data (table and size)
|
||||
* rep: enables key autorepeat
|
||||
*
|
||||
* This structure is supposed to be used by platform code to supply
|
||||
* keymaps to drivers that implement keyboards.
|
||||
*/
|
||||
struct kbd_platform_data {
|
||||
const struct matrix_keymap_data *keymap;
|
||||
bool rep;
|
||||
};
|
||||
|
||||
/* This function is used to set platform data field of pdev->dev */
|
||||
static inline void
|
||||
kbd_set_plat_data(struct platform_device *pdev, struct kbd_platform_data *data)
|
||||
{
|
||||
pdev->dev.platform_data = data;
|
||||
}
|
||||
|
||||
#endif /* __PLAT_KEYBOARD_H */
|
@ -154,7 +154,8 @@ config HID_EGALAX
|
||||
tristate "eGalax multi-touch panel"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for the eGalax dual-touch panel.
|
||||
Support for the eGalax dual-touch panels, including the
|
||||
Joojoo and Wetab tablets.
|
||||
|
||||
config HID_ELECOM
|
||||
tristate "ELECOM BM084 bluetooth mouse"
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
|
||||
MODULE_DESCRIPTION("3M PCT multitouch panels");
|
||||
@ -27,8 +28,6 @@ MODULE_LICENSE("GPL");
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MAX_SLOTS 60
|
||||
#define MAX_TRKID USHRT_MAX
|
||||
#define MAX_EVENTS 360
|
||||
|
||||
/* estimated signal-to-noise ratios */
|
||||
#define SN_MOVE 2048
|
||||
@ -36,14 +35,11 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
struct mmm_finger {
|
||||
__s32 x, y, w, h;
|
||||
__u16 id;
|
||||
bool prev_touch;
|
||||
bool touch, valid;
|
||||
};
|
||||
|
||||
struct mmm_data {
|
||||
struct mmm_finger f[MAX_SLOTS];
|
||||
__u16 id;
|
||||
__u8 curid;
|
||||
__u8 nexp, nreal;
|
||||
bool touch, valid;
|
||||
@ -117,14 +113,7 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
0, 1, 0, 0);
|
||||
return 1;
|
||||
case HID_DG_CONTACTID:
|
||||
field->logical_maximum = MAX_TRKID;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TRACKING_ID);
|
||||
input_set_abs_params(hi->input, ABS_MT_TRACKING_ID,
|
||||
0, MAX_TRKID, 0, 0);
|
||||
if (!hi->input->mt)
|
||||
input_mt_create_slots(hi->input, MAX_SLOTS);
|
||||
input_set_events_per_packet(hi->input, MAX_EVENTS);
|
||||
input_mt_init_slots(hi->input, MAX_SLOTS);
|
||||
return 1;
|
||||
}
|
||||
/* let hid-input decide for the others */
|
||||
@ -154,7 +143,6 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
*/
|
||||
static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
|
||||
{
|
||||
struct mmm_finger *oldest = 0;
|
||||
int i;
|
||||
for (i = 0; i < MAX_SLOTS; ++i) {
|
||||
struct mmm_finger *f = &md->f[i];
|
||||
@ -163,6 +151,7 @@ static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
|
||||
continue;
|
||||
}
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, f->touch);
|
||||
if (f->touch) {
|
||||
/* this finger is on the screen */
|
||||
int wide = (f->w > f->h);
|
||||
@ -170,33 +159,16 @@ static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
|
||||
int major = max(f->w, f->h) >> 1;
|
||||
int minor = min(f->w, f->h) >> 1;
|
||||
|
||||
if (!f->prev_touch)
|
||||
f->id = md->id++;
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
|
||||
input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
|
||||
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
|
||||
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
|
||||
/* touchscreen emulation: pick the oldest contact */
|
||||
if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1)))
|
||||
oldest = f;
|
||||
} else {
|
||||
/* this finger took off the screen */
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
}
|
||||
f->prev_touch = f->touch;
|
||||
f->valid = 0;
|
||||
}
|
||||
|
||||
/* touchscreen emulation */
|
||||
if (oldest) {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||
input_event(input, EV_ABS, ABS_X, oldest->x);
|
||||
input_event(input, EV_ABS, ABS_Y, oldest->y);
|
||||
} else {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 0);
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
|
@ -1300,6 +1300,9 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
|
||||
|
@ -2,6 +2,8 @@
|
||||
* HID driver for eGalax dual-touch panels
|
||||
*
|
||||
* Copyright (c) 2010 Stephane Chatty <chatty@enac.fr>
|
||||
* Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se>
|
||||
* Copyright (c) 2010 Canonical, Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
@ -16,6 +18,7 @@
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/slab.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
@ -25,38 +28,53 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MAX_SLOTS 2
|
||||
|
||||
/* estimated signal-to-noise ratios */
|
||||
#define SN_MOVE 4096
|
||||
#define SN_PRESSURE 32
|
||||
|
||||
struct egalax_data {
|
||||
__u16 x, y, z;
|
||||
__u8 id;
|
||||
bool first; /* is this the first finger in the frame? */
|
||||
bool valid; /* valid finger data, or just placeholder? */
|
||||
bool activity; /* at least one active finger previously? */
|
||||
__u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */
|
||||
int valid;
|
||||
int slot;
|
||||
int touch;
|
||||
int x, y, z;
|
||||
};
|
||||
|
||||
static void set_abs(struct input_dev *input, unsigned int code,
|
||||
struct hid_field *field, int snratio)
|
||||
{
|
||||
int fmin = field->logical_minimum;
|
||||
int fmax = field->logical_maximum;
|
||||
int fuzz = snratio ? (fmax - fmin) / snratio : 0;
|
||||
input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
|
||||
}
|
||||
|
||||
static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct input_dev *input = hi->input;
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_GENDESK:
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_X);
|
||||
set_abs(input, ABS_MT_POSITION_X, field, SN_MOVE);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_X,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
set_abs(input, ABS_X, field, SN_MOVE);
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_Y);
|
||||
set_abs(input, ABS_MT_POSITION_Y, field, SN_MOVE);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_Y,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
set_abs(input, ABS_Y, field, SN_MOVE);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -66,6 +84,7 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
case HID_DG_TIPSWITCH:
|
||||
/* touchscreen emulation */
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
case HID_DG_INRANGE:
|
||||
case HID_DG_CONFIDENCE:
|
||||
@ -73,16 +92,15 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
case HID_DG_CONTACTMAX:
|
||||
return -1;
|
||||
case HID_DG_CONTACTID:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TRACKING_ID);
|
||||
input_mt_init_slots(input, MAX_SLOTS);
|
||||
return 1;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
field->logical_minimum = 0;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_PRESSURE);
|
||||
set_abs(input, ABS_MT_PRESSURE, field, SN_PRESSURE);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_PRESSURE,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
set_abs(input, ABS_PRESSURE, field, SN_PRESSURE);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -96,10 +114,10 @@ static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
/* tell hid-input to skip setup of these event types */
|
||||
if (usage->type == EV_KEY || usage->type == EV_ABS)
|
||||
clear_bit(usage->code, *bit);
|
||||
|
||||
return 0;
|
||||
set_bit(usage->type, hi->input->evbit);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -108,58 +126,16 @@ static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
*/
|
||||
static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
|
||||
{
|
||||
td->first = !td->first; /* touchscreen emulation */
|
||||
|
||||
if (td->valid) {
|
||||
/* emit multitouch events */
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3);
|
||||
input_mt_slot(input, td->slot);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, td->touch);
|
||||
if (td->touch) {
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
|
||||
input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
|
||||
|
||||
input_mt_sync(input);
|
||||
|
||||
/*
|
||||
* touchscreen emulation: store (x, y) as
|
||||
* the last valid values in this frame
|
||||
*/
|
||||
td->lastx = td->x;
|
||||
td->lasty = td->y;
|
||||
td->lastz = td->z;
|
||||
}
|
||||
|
||||
/*
|
||||
* touchscreen emulation: if this is the second finger and at least
|
||||
* one in this frame is valid, the latest valid in the frame is
|
||||
* the oldest on the panel, the one we want for single touch
|
||||
*/
|
||||
if (!td->first && td->activity) {
|
||||
input_event(input, EV_ABS, ABS_X, td->lastx >> 3);
|
||||
input_event(input, EV_ABS, ABS_Y, td->lasty >> 3);
|
||||
input_event(input, EV_ABS, ABS_PRESSURE, td->lastz);
|
||||
}
|
||||
|
||||
if (!td->valid) {
|
||||
/*
|
||||
* touchscreen emulation: if the first finger is invalid
|
||||
* and there previously was finger activity, this is a release
|
||||
*/
|
||||
if (td->first && td->activity) {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 0);
|
||||
td->activity = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* touchscreen emulation: if no previous activity, emit touch event */
|
||||
if (!td->activity) {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||
td->activity = true;
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
}
|
||||
|
||||
|
||||
static int egalax_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
@ -169,25 +145,26 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field,
|
||||
* uses a standard parallel multitouch protocol (product ID ==
|
||||
* 48xx). The second is capacitive and uses an unusual "serial"
|
||||
* protocol with a different message for each multitouch finger
|
||||
* (product ID == 72xx). We do not yet generate a correct event
|
||||
* sequence for the capacitive/serial protocol.
|
||||
* (product ID == 72xx).
|
||||
*/
|
||||
if (hid->claimed & HID_CLAIMED_INPUT) {
|
||||
struct input_dev *input = field->hidinput->input;
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_DG_INRANGE:
|
||||
td->valid = value;
|
||||
break;
|
||||
case HID_DG_CONFIDENCE:
|
||||
/* avoid interference from generic hidinput handling */
|
||||
break;
|
||||
case HID_DG_TIPSWITCH:
|
||||
td->valid = value;
|
||||
td->touch = value;
|
||||
break;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
td->z = value;
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
td->id = value;
|
||||
td->slot = clamp_val(value, 0, MAX_SLOTS - 1);
|
||||
break;
|
||||
case HID_GD_X:
|
||||
td->x = value;
|
||||
@ -195,11 +172,11 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field,
|
||||
case HID_GD_Y:
|
||||
td->y = value;
|
||||
/* this is the last field in a finger */
|
||||
egalax_filter_event(td, input);
|
||||
if (td->valid)
|
||||
egalax_filter_event(td, input);
|
||||
break;
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
/* touch emulation: this is the last field in a frame */
|
||||
td->first = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -261,6 +238,12 @@ static const struct hid_device_id egalax_devices[] = {
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, egalax_devices);
|
||||
|
@ -196,6 +196,9 @@
|
||||
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 0x726b
|
||||
|
||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
|
||||
|
@ -5,7 +5,7 @@
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_INPUT) += input-core.o
|
||||
input-core-objs := input.o input-compat.o ff-core.o
|
||||
input-core-y := input.o input-compat.o input-mt.o ff-core.o
|
||||
|
||||
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
|
||||
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
|
||||
|
@ -9,6 +9,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
@ -23,8 +25,7 @@ static void system_power_event(unsigned int keycode)
|
||||
switch (keycode) {
|
||||
case KEY_SUSPEND:
|
||||
apm_queue_event(APM_USER_SUSPEND);
|
||||
|
||||
printk(KERN_INFO "apm-power: Requesting system suspend...\n");
|
||||
pr_info("Requesting system suspend...\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -65,18 +66,15 @@ static int apmpower_connect(struct input_handler *handler,
|
||||
|
||||
error = input_register_handle(handle);
|
||||
if (error) {
|
||||
printk(KERN_ERR
|
||||
"apm-power: Failed to register input power handler, "
|
||||
"error %d\n", error);
|
||||
pr_err("Failed to register input power handler, error %d\n",
|
||||
error);
|
||||
kfree(handle);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_open_device(handle);
|
||||
if (error) {
|
||||
printk(KERN_ERR
|
||||
"apm-power: Failed to open input power device, "
|
||||
"error %d\n", error);
|
||||
pr_err("Failed to open input power device, error %d\n", error);
|
||||
input_unregister_handle(handle);
|
||||
kfree(handle);
|
||||
return error;
|
||||
|
@ -26,6 +26,8 @@
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
@ -38,8 +40,8 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
|
||||
dev_name(&handle->dev->dev), type, code, value);
|
||||
printk(KERN_DEBUG pr_fmt("Event. Dev: %s, Type: %d, Code: %d, Value: %d\n"),
|
||||
dev_name(&handle->dev->dev), type, code, value);
|
||||
}
|
||||
|
||||
static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
@ -64,10 +66,10 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
if (error)
|
||||
goto err_unregister_handle;
|
||||
|
||||
printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n",
|
||||
dev_name(&dev->dev),
|
||||
dev->name ?: "unknown",
|
||||
dev->phys ?: "unknown");
|
||||
printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"),
|
||||
dev_name(&dev->dev),
|
||||
dev->name ?: "unknown",
|
||||
dev->phys ?: "unknown");
|
||||
|
||||
return 0;
|
||||
|
||||
@ -80,8 +82,8 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
|
||||
static void evbug_disconnect(struct input_handle *handle)
|
||||
{
|
||||
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n",
|
||||
dev_name(&handle->dev->dev));
|
||||
printk(KERN_DEBUG pr_fmt("Disconnected device: %s\n"),
|
||||
dev_name(&handle->dev->dev));
|
||||
|
||||
input_close_device(handle);
|
||||
input_unregister_handle(handle);
|
||||
|
@ -8,6 +8,8 @@
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define EVDEV_MINOR_BASE 64
|
||||
#define EVDEV_MINORS 32
|
||||
#define EVDEV_MIN_BUFFER_SIZE 64U
|
||||
@ -522,12 +524,11 @@ static int handle_eviocgbit(struct input_dev *dev,
|
||||
if (type == EV_KEY && size == OLD_KEY_MAX) {
|
||||
len = OLD_KEY_MAX;
|
||||
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
|
||||
printk(KERN_WARNING
|
||||
"evdev.c(EVIOCGBIT): Suspicious buffer size %u, "
|
||||
"limiting output to %zu bytes. See "
|
||||
"http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
|
||||
OLD_KEY_MAX,
|
||||
BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
|
||||
pr_warning("(EVIOCGBIT): Suspicious buffer size %u, "
|
||||
"limiting output to %zu bytes. See "
|
||||
"http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
|
||||
OLD_KEY_MAX,
|
||||
BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
|
||||
}
|
||||
|
||||
return bits_to_user(bits, len, size, p, compat_mode);
|
||||
@ -686,6 +687,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
||||
switch (EVIOC_MASK_SIZE(cmd)) {
|
||||
|
||||
case EVIOCGPROP(0):
|
||||
return bits_to_user(dev->propbit, INPUT_PROP_MAX,
|
||||
size, p, compat_mode);
|
||||
|
||||
case EVIOCGKEY(0):
|
||||
return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
|
||||
|
||||
@ -897,7 +902,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
break;
|
||||
|
||||
if (minor == EVDEV_MINORS) {
|
||||
printk(KERN_ERR "evdev: no more free evdev devices\n");
|
||||
pr_err("no more free evdev devices\n");
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg)
|
||||
#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
@ -116,7 +116,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
|
||||
|
||||
if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
|
||||
!test_bit(effect->type, dev->ffbit)) {
|
||||
debug("invalid or not supported effect type in upload");
|
||||
pr_debug("invalid or not supported effect type in upload\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
|
||||
(effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
|
||||
effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
|
||||
!test_bit(effect->u.periodic.waveform, dev->ffbit))) {
|
||||
debug("invalid or not supported wave form in upload");
|
||||
pr_debug("invalid or not supported wave form in upload\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -246,7 +246,7 @@ static int flush_effects(struct input_dev *dev, struct file *file)
|
||||
struct ff_device *ff = dev->ff;
|
||||
int i;
|
||||
|
||||
debug("flushing now");
|
||||
pr_debug("flushing now\n");
|
||||
|
||||
mutex_lock(&ff->mutex);
|
||||
|
||||
@ -315,8 +315,7 @@ int input_ff_create(struct input_dev *dev, int max_effects)
|
||||
int i;
|
||||
|
||||
if (!max_effects) {
|
||||
printk(KERN_ERR
|
||||
"ff-core: cannot allocate device without any effects\n");
|
||||
pr_err("cannot allocate device without any effects\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg)
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
@ -129,7 +129,7 @@ static void ml_schedule_timer(struct ml_device *ml)
|
||||
int events = 0;
|
||||
int i;
|
||||
|
||||
debug("calculating next timer");
|
||||
pr_debug("calculating next timer\n");
|
||||
|
||||
for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
|
||||
|
||||
@ -149,10 +149,10 @@ static void ml_schedule_timer(struct ml_device *ml)
|
||||
}
|
||||
|
||||
if (!events) {
|
||||
debug("no actions");
|
||||
pr_debug("no actions\n");
|
||||
del_timer(&ml->timer);
|
||||
} else {
|
||||
debug("timer set");
|
||||
pr_debug("timer set\n");
|
||||
mod_timer(&ml->timer, earliest);
|
||||
}
|
||||
}
|
||||
@ -173,8 +173,8 @@ static int apply_envelope(struct ml_effect_state *state, int value,
|
||||
if (envelope->attack_length &&
|
||||
time_before(now,
|
||||
state->play_at + msecs_to_jiffies(envelope->attack_length))) {
|
||||
debug("value = 0x%x, attack_level = 0x%x", value,
|
||||
envelope->attack_level);
|
||||
pr_debug("value = 0x%x, attack_level = 0x%x\n",
|
||||
value, envelope->attack_level);
|
||||
time_from_level = jiffies_to_msecs(now - state->play_at);
|
||||
time_of_envelope = envelope->attack_length;
|
||||
envelope_level = min_t(__s16, envelope->attack_level, 0x7fff);
|
||||
@ -191,13 +191,13 @@ static int apply_envelope(struct ml_effect_state *state, int value,
|
||||
|
||||
difference = abs(value) - envelope_level;
|
||||
|
||||
debug("difference = %d", difference);
|
||||
debug("time_from_level = 0x%x", time_from_level);
|
||||
debug("time_of_envelope = 0x%x", time_of_envelope);
|
||||
pr_debug("difference = %d\n", difference);
|
||||
pr_debug("time_from_level = 0x%x\n", time_from_level);
|
||||
pr_debug("time_of_envelope = 0x%x\n", time_of_envelope);
|
||||
|
||||
difference = difference * time_from_level / time_of_envelope;
|
||||
|
||||
debug("difference = %d", difference);
|
||||
pr_debug("difference = %d\n", difference);
|
||||
|
||||
return value < 0 ?
|
||||
-(difference + envelope_level) : (difference + envelope_level);
|
||||
@ -215,8 +215,7 @@ static int get_compatible_type(struct ff_device *ff, int effect_type)
|
||||
if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
|
||||
return FF_RUMBLE;
|
||||
|
||||
printk(KERN_ERR
|
||||
"ff-memless: invalid type in get_compatible_type()\n");
|
||||
pr_err("invalid type in get_compatible_type()\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -312,7 +311,7 @@ static void ml_combine_effects(struct ff_effect *effect,
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n");
|
||||
pr_err("invalid type in ml_combine_effects()\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -406,7 +405,7 @@ static void ml_effect_timer(unsigned long timer_data)
|
||||
struct ml_device *ml = dev->ff->private;
|
||||
unsigned long flags;
|
||||
|
||||
debug("timer: updating effects");
|
||||
pr_debug("timer: updating effects\n");
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
ml_play_effects(ml);
|
||||
@ -438,7 +437,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
|
||||
struct ml_effect_state *state = &ml->states[effect_id];
|
||||
|
||||
if (value > 0) {
|
||||
debug("initiated play");
|
||||
pr_debug("initiated play\n");
|
||||
|
||||
__set_bit(FF_EFFECT_STARTED, &state->flags);
|
||||
state->count = value;
|
||||
@ -449,7 +448,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
|
||||
state->adj_at = state->play_at;
|
||||
|
||||
} else {
|
||||
debug("initiated stop");
|
||||
pr_debug("initiated stop\n");
|
||||
|
||||
if (test_bit(FF_EFFECT_PLAYING, &state->flags))
|
||||
__set_bit(FF_EFFECT_ABORTING, &state->flags);
|
||||
|
@ -18,13 +18,11 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gameport.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/sched.h> /* HZ */
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
/*#include <asm/io.h>*/
|
||||
|
||||
@ -234,58 +232,22 @@ struct gameport_event {
|
||||
|
||||
static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */
|
||||
static LIST_HEAD(gameport_event_list);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
|
||||
static struct task_struct *gameport_task;
|
||||
|
||||
static int gameport_queue_event(void *object, struct module *owner,
|
||||
enum gameport_event_type event_type)
|
||||
static struct gameport_event *gameport_get_event(void)
|
||||
{
|
||||
struct gameport_event *event = NULL;
|
||||
unsigned long flags;
|
||||
struct gameport_event *event;
|
||||
int retval = 0;
|
||||
|
||||
spin_lock_irqsave(&gameport_event_lock, flags);
|
||||
|
||||
/*
|
||||
* Scan event list for the other events for the same gameport port,
|
||||
* starting with the most recent one. If event is the same we
|
||||
* do not need add new one. If event is of different type we
|
||||
* need to add this event and should not look further because
|
||||
* we need to preseve sequence of distinct events.
|
||||
*/
|
||||
list_for_each_entry_reverse(event, &gameport_event_list, node) {
|
||||
if (event->object == object) {
|
||||
if (event->type == event_type)
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
if (!list_empty(&gameport_event_list)) {
|
||||
event = list_first_entry(&gameport_event_list,
|
||||
struct gameport_event, node);
|
||||
list_del_init(&event->node);
|
||||
}
|
||||
|
||||
event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
|
||||
if (!event) {
|
||||
pr_err("Not enough memory to queue event %d\n", event_type);
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!try_module_get(owner)) {
|
||||
pr_warning("Can't get module reference, dropping event %d\n",
|
||||
event_type);
|
||||
kfree(event);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
event->type = event_type;
|
||||
event->object = object;
|
||||
event->owner = owner;
|
||||
|
||||
list_add_tail(&event->node, &gameport_event_list);
|
||||
wake_up(&gameport_wait);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gameport_event_lock, flags);
|
||||
return retval;
|
||||
return event;
|
||||
}
|
||||
|
||||
static void gameport_free_event(struct gameport_event *event)
|
||||
@ -319,24 +281,8 @@ static void gameport_remove_duplicate_events(struct gameport_event *event)
|
||||
spin_unlock_irqrestore(&gameport_event_lock, flags);
|
||||
}
|
||||
|
||||
static struct gameport_event *gameport_get_event(void)
|
||||
{
|
||||
struct gameport_event *event = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gameport_event_lock, flags);
|
||||
|
||||
if (!list_empty(&gameport_event_list)) {
|
||||
event = list_first_entry(&gameport_event_list,
|
||||
struct gameport_event, node);
|
||||
list_del_init(&event->node);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&gameport_event_lock, flags);
|
||||
return event;
|
||||
}
|
||||
|
||||
static void gameport_handle_event(void)
|
||||
static void gameport_handle_events(struct work_struct *work)
|
||||
{
|
||||
struct gameport_event *event;
|
||||
|
||||
@ -368,6 +314,59 @@ static void gameport_handle_event(void)
|
||||
mutex_unlock(&gameport_mutex);
|
||||
}
|
||||
|
||||
static DECLARE_WORK(gameport_event_work, gameport_handle_events);
|
||||
|
||||
static int gameport_queue_event(void *object, struct module *owner,
|
||||
enum gameport_event_type event_type)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct gameport_event *event;
|
||||
int retval = 0;
|
||||
|
||||
spin_lock_irqsave(&gameport_event_lock, flags);
|
||||
|
||||
/*
|
||||
* Scan event list for the other events for the same gameport port,
|
||||
* starting with the most recent one. If event is the same we
|
||||
* do not need add new one. If event is of different type we
|
||||
* need to add this event and should not look further because
|
||||
* we need to preserve sequence of distinct events.
|
||||
*/
|
||||
list_for_each_entry_reverse(event, &gameport_event_list, node) {
|
||||
if (event->object == object) {
|
||||
if (event->type == event_type)
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
|
||||
if (!event) {
|
||||
pr_err("Not enough memory to queue event %d\n", event_type);
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!try_module_get(owner)) {
|
||||
pr_warning("Can't get module reference, dropping event %d\n",
|
||||
event_type);
|
||||
kfree(event);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
event->type = event_type;
|
||||
event->object = object;
|
||||
event->owner = owner;
|
||||
|
||||
list_add_tail(&event->node, &gameport_event_list);
|
||||
schedule_work(&gameport_event_work);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gameport_event_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all events that have been submitted for a given object,
|
||||
* be it a gameport port or a driver.
|
||||
@ -419,19 +418,6 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent)
|
||||
return child;
|
||||
}
|
||||
|
||||
static int gameport_thread(void *nothing)
|
||||
{
|
||||
set_freezable();
|
||||
do {
|
||||
gameport_handle_event();
|
||||
wait_event_freezable(gameport_wait,
|
||||
kthread_should_stop() || !list_empty(&gameport_event_list));
|
||||
} while (!kthread_should_stop());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Gameport port operations
|
||||
*/
|
||||
@ -814,13 +800,6 @@ static int __init gameport_init(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
gameport_task = kthread_run(gameport_thread, NULL, "kgameportd");
|
||||
if (IS_ERR(gameport_task)) {
|
||||
bus_unregister(&gameport_bus);
|
||||
error = PTR_ERR(gameport_task);
|
||||
pr_err("Failed to start kgameportd, error: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -828,7 +807,12 @@ static int __init gameport_init(void)
|
||||
static void __exit gameport_exit(void)
|
||||
{
|
||||
bus_unregister(&gameport_bus);
|
||||
kthread_stop(gameport_task);
|
||||
|
||||
/*
|
||||
* There should not be any outstanding events but work may
|
||||
* still be scheduled so simply cancel it.
|
||||
*/
|
||||
cancel_work_sync(&gameport_event_work);
|
||||
}
|
||||
|
||||
subsys_initcall(gameport_init);
|
||||
|
170
drivers/input/input-mt.c
Normal file
170
drivers/input/input-mt.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Input Multitouch Library
|
||||
*
|
||||
* Copyright (c) 2008-2010 Henrik Rydberg
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
|
||||
|
||||
/**
|
||||
* input_mt_init_slots() - initialize MT input slots
|
||||
* @dev: input device supporting MT events and finger tracking
|
||||
* @num_slots: number of slots used by the device
|
||||
*
|
||||
* This function allocates all necessary memory for MT slot handling
|
||||
* in the input device, prepares the ABS_MT_SLOT and
|
||||
* ABS_MT_TRACKING_ID events for use and sets up appropriate buffers.
|
||||
* May be called repeatedly. Returns -EINVAL if attempting to
|
||||
* reinitialize with a different number of slots.
|
||||
*/
|
||||
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!num_slots)
|
||||
return 0;
|
||||
if (dev->mt)
|
||||
return dev->mtsize != num_slots ? -EINVAL : 0;
|
||||
|
||||
dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
|
||||
if (!dev->mt)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->mtsize = num_slots;
|
||||
input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
|
||||
input_set_events_per_packet(dev, 6 * num_slots);
|
||||
|
||||
/* Mark slots as 'unused' */
|
||||
for (i = 0; i < num_slots; i++)
|
||||
input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_init_slots);
|
||||
|
||||
/**
|
||||
* input_mt_destroy_slots() - frees the MT slots of the input device
|
||||
* @dev: input device with allocated MT slots
|
||||
*
|
||||
* This function is only needed in error path as the input core will
|
||||
* automatically free the MT slots when the device is destroyed.
|
||||
*/
|
||||
void input_mt_destroy_slots(struct input_dev *dev)
|
||||
{
|
||||
kfree(dev->mt);
|
||||
dev->mt = NULL;
|
||||
dev->mtsize = 0;
|
||||
dev->slot = 0;
|
||||
dev->trkid = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_destroy_slots);
|
||||
|
||||
/**
|
||||
* input_mt_report_slot_state() - report contact state
|
||||
* @dev: input device with allocated MT slots
|
||||
* @tool_type: the tool type to use in this slot
|
||||
* @active: true if contact is active, false otherwise
|
||||
*
|
||||
* Reports a contact via ABS_MT_TRACKING_ID, and optionally
|
||||
* ABS_MT_TOOL_TYPE. If active is true and the slot is currently
|
||||
* inactive, or if the tool type is changed, a new tracking id is
|
||||
* assigned to the slot. The tool type is only reported if the
|
||||
* corresponding absbit field is set.
|
||||
*/
|
||||
void input_mt_report_slot_state(struct input_dev *dev,
|
||||
unsigned int tool_type, bool active)
|
||||
{
|
||||
struct input_mt_slot *mt;
|
||||
int id;
|
||||
|
||||
if (!dev->mt || !active) {
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
mt = &dev->mt[dev->slot];
|
||||
id = input_mt_get_value(mt, ABS_MT_TRACKING_ID);
|
||||
if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type)
|
||||
id = input_mt_new_trkid(dev);
|
||||
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
|
||||
input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_report_slot_state);
|
||||
|
||||
/**
|
||||
* input_mt_report_finger_count() - report contact count
|
||||
* @dev: input device with allocated MT slots
|
||||
* @count: the number of contacts
|
||||
*
|
||||
* Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP,
|
||||
* BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP.
|
||||
*
|
||||
* The input core ensures only the KEY events already setup for
|
||||
* this device will produce output.
|
||||
*/
|
||||
void input_mt_report_finger_count(struct input_dev *dev, int count)
|
||||
{
|
||||
input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1);
|
||||
input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2);
|
||||
input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3);
|
||||
input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4);
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_report_finger_count);
|
||||
|
||||
/**
|
||||
* input_mt_report_pointer_emulation() - common pointer emulation
|
||||
* @dev: input device with allocated MT slots
|
||||
* @use_count: report number of active contacts as finger count
|
||||
*
|
||||
* Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and
|
||||
* ABS_PRESSURE. Touchpad finger count is emulated if use_count is true.
|
||||
*
|
||||
* The input core ensures only the KEY and ABS axes already setup for
|
||||
* this device will produce output.
|
||||
*/
|
||||
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
|
||||
{
|
||||
struct input_mt_slot *oldest = 0;
|
||||
int oldid = dev->trkid;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->mtsize; ++i) {
|
||||
struct input_mt_slot *ps = &dev->mt[i];
|
||||
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
|
||||
|
||||
if (id < 0)
|
||||
continue;
|
||||
if ((id - oldid) & TRKID_SGN) {
|
||||
oldest = ps;
|
||||
oldid = id;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
|
||||
if (use_count)
|
||||
input_mt_report_finger_count(dev, count);
|
||||
|
||||
if (oldest) {
|
||||
int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
|
||||
int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
|
||||
int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
|
||||
|
||||
input_event(dev, EV_ABS, ABS_X, x);
|
||||
input_event(dev, EV_ABS, ABS_Y, y);
|
||||
input_event(dev, EV_ABS, ABS_PRESSURE, p);
|
||||
} else {
|
||||
input_event(dev, EV_ABS, ABS_PRESSURE, 0);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_report_pointer_emulation);
|
@ -8,6 +8,8 @@
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
@ -33,8 +35,7 @@ static int input_polldev_start_workqueue(void)
|
||||
if (!polldev_users) {
|
||||
polldev_wq = create_singlethread_workqueue("ipolldevd");
|
||||
if (!polldev_wq) {
|
||||
printk(KERN_ERR "input-polldev: failed to create "
|
||||
"ipolldevd workqueue\n");
|
||||
pr_err("failed to create ipolldevd workqueue\n");
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
@ -10,9 +10,11 @@
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
@ -958,10 +960,8 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han
|
||||
|
||||
error = handler->connect(handler, dev, id);
|
||||
if (error && error != -ENODEV)
|
||||
printk(KERN_ERR
|
||||
"input: failed to attach handler %s to device %s, "
|
||||
"error: %d\n",
|
||||
handler->name, kobject_name(&dev->dev.kobj), error);
|
||||
pr_err("failed to attach handler %s to device %s, error: %d\n",
|
||||
handler->name, kobject_name(&dev->dev.kobj), error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1109,6 +1109,8 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
|
||||
seq_printf(seq, "%s ", handle->name);
|
||||
seq_putc(seq, '\n');
|
||||
|
||||
input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX);
|
||||
|
||||
input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX);
|
||||
if (test_bit(EV_KEY, dev->evbit))
|
||||
input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX);
|
||||
@ -1332,11 +1334,26 @@ static ssize_t input_dev_show_modalias(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);
|
||||
|
||||
static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap,
|
||||
int max, int add_cr);
|
||||
|
||||
static ssize_t input_dev_show_properties(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct input_dev *input_dev = to_input_dev(dev);
|
||||
int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit,
|
||||
INPUT_PROP_MAX, true);
|
||||
return min_t(int, len, PAGE_SIZE);
|
||||
}
|
||||
static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL);
|
||||
|
||||
static struct attribute *input_dev_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_phys.attr,
|
||||
&dev_attr_uniq.attr,
|
||||
&dev_attr_modalias.attr,
|
||||
&dev_attr_properties.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -1470,7 +1487,7 @@ static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
|
||||
{
|
||||
int len;
|
||||
|
||||
if (add_uevent_var(env, "%s=", name))
|
||||
if (add_uevent_var(env, "%s", name))
|
||||
return -ENOMEM;
|
||||
|
||||
len = input_print_bitmap(&env->buf[env->buflen - 1],
|
||||
@ -1536,6 +1553,8 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
|
||||
if (dev->uniq)
|
||||
INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq);
|
||||
|
||||
INPUT_ADD_HOTPLUG_BM_VAR("PROP=", dev->propbit, INPUT_PROP_MAX);
|
||||
|
||||
INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX);
|
||||
if (test_bit(EV_KEY, dev->evbit))
|
||||
INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX);
|
||||
@ -1724,52 +1743,6 @@ void input_free_device(struct input_dev *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(input_free_device);
|
||||
|
||||
/**
|
||||
* input_mt_create_slots() - create MT input slots
|
||||
* @dev: input device supporting MT events and finger tracking
|
||||
* @num_slots: number of slots used by the device
|
||||
*
|
||||
* This function allocates all necessary memory for MT slot handling in the
|
||||
* input device, and adds ABS_MT_SLOT to the device capabilities. All slots
|
||||
* are initially marked as unused by setting ABS_MT_TRACKING_ID to -1.
|
||||
*/
|
||||
int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!num_slots)
|
||||
return 0;
|
||||
|
||||
dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
|
||||
if (!dev->mt)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->mtsize = num_slots;
|
||||
input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
|
||||
|
||||
/* Mark slots as 'unused' */
|
||||
for (i = 0; i < num_slots; i++)
|
||||
dev->mt[i].abs[ABS_MT_TRACKING_ID - ABS_MT_FIRST] = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_create_slots);
|
||||
|
||||
/**
|
||||
* input_mt_destroy_slots() - frees the MT slots of the input device
|
||||
* @dev: input device with allocated MT slots
|
||||
*
|
||||
* This function is only needed in error path as the input core will
|
||||
* automatically free the MT slots when the device is destroyed.
|
||||
*/
|
||||
void input_mt_destroy_slots(struct input_dev *dev)
|
||||
{
|
||||
kfree(dev->mt);
|
||||
dev->mt = NULL;
|
||||
dev->mtsize = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_destroy_slots);
|
||||
|
||||
/**
|
||||
* input_set_capability - mark device as capable of a certain event
|
||||
* @dev: device that is capable of emitting or accepting event
|
||||
@ -1819,9 +1792,8 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"input_set_capability: unknown type %u (code %u)\n",
|
||||
type, code);
|
||||
pr_err("input_set_capability: unknown type %u (code %u)\n",
|
||||
type, code);
|
||||
dump_stack();
|
||||
return;
|
||||
}
|
||||
@ -1903,8 +1875,9 @@ int input_register_device(struct input_dev *dev)
|
||||
return error;
|
||||
|
||||
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
|
||||
printk(KERN_INFO "input: %s as %s\n",
|
||||
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
|
||||
pr_info("%s as %s\n",
|
||||
dev->name ? dev->name : "Unspecified device",
|
||||
path ? path : "N/A");
|
||||
kfree(path);
|
||||
|
||||
error = mutex_lock_interruptible(&input_mutex);
|
||||
@ -2186,7 +2159,7 @@ static int __init input_init(void)
|
||||
|
||||
err = class_register(&input_class);
|
||||
if (err) {
|
||||
printk(KERN_ERR "input: unable to register input_dev class\n");
|
||||
pr_err("unable to register input_dev class\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -2196,7 +2169,7 @@ static int __init input_init(void)
|
||||
|
||||
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
|
||||
if (err) {
|
||||
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
|
||||
pr_err("unable to register char major %d", INPUT_MAJOR);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <linux/delay.h>
|
||||
@ -806,7 +808,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
break;
|
||||
|
||||
if (minor == JOYDEV_MINORS) {
|
||||
printk(KERN_ERR "joydev: no more free joydev devices\n");
|
||||
pr_err("no more free joydev devices\n");
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,8 @@
|
||||
# By Johann Deneux <johann.deneux@gmail.com>
|
||||
#
|
||||
|
||||
# Goal definition
|
||||
iforce-objs := iforce-ff.o iforce-main.o iforce-packets.o
|
||||
|
||||
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
|
||||
|
||||
ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
|
||||
iforce-objs += iforce-serio.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
|
||||
iforce-objs += iforce-usb.o
|
||||
endif
|
||||
|
||||
EXTRA_CFLAGS = -Werror-implicit-function-declaration
|
||||
iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
|
||||
iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o
|
||||
iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
|
||||
|
@ -543,21 +543,25 @@ exit:
|
||||
static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
|
||||
{
|
||||
struct usb_endpoint_descriptor *ep_irq_out;
|
||||
int error = -ENOMEM;
|
||||
int error;
|
||||
|
||||
if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX)
|
||||
return 0;
|
||||
|
||||
xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
|
||||
GFP_KERNEL, &xpad->odata_dma);
|
||||
if (!xpad->odata)
|
||||
if (!xpad->odata) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
mutex_init(&xpad->odata_mutex);
|
||||
|
||||
xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!xpad->irq_out)
|
||||
if (!xpad->irq_out) {
|
||||
error = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
ep_irq_out = &intf->cur_altsetting->endpoint[1].desc;
|
||||
usb_fill_int_urb(xpad->irq_out, xpad->udev,
|
||||
@ -728,7 +732,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
|
||||
|
||||
if (xpad_led) {
|
||||
led_classdev_unregister(&xpad_led->led_cdev);
|
||||
kfree(xpad_led->name);
|
||||
kfree(xpad_led);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -756,8 +760,9 @@ static void xpad_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_xpad *xpad = input_get_drvdata(dev);
|
||||
|
||||
if(xpad->xtype != XTYPE_XBOX360W)
|
||||
if (xpad->xtype != XTYPE_XBOX360W)
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
|
||||
xpad_stop_output(xpad);
|
||||
}
|
||||
|
||||
@ -789,8 +794,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
struct usb_xpad *xpad;
|
||||
struct input_dev *input_dev;
|
||||
struct usb_endpoint_descriptor *ep_irq_in;
|
||||
int i;
|
||||
int error = -ENOMEM;
|
||||
int i, error;
|
||||
|
||||
for (i = 0; xpad_device[i].idVendor; i++) {
|
||||
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
|
||||
@ -800,17 +804,23 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
|
||||
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!xpad || !input_dev)
|
||||
if (!xpad || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
|
||||
GFP_KERNEL, &xpad->idata_dma);
|
||||
if (!xpad->idata)
|
||||
if (!xpad->idata) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!xpad->irq_in)
|
||||
if (!xpad->irq_in) {
|
||||
error = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
xpad->udev = udev;
|
||||
xpad->mapping = xpad_device[i].mapping;
|
||||
@ -887,15 +897,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
|
||||
error = xpad_init_output(intf, xpad);
|
||||
if (error)
|
||||
goto fail2;
|
||||
goto fail3;
|
||||
|
||||
error = xpad_init_ff(xpad);
|
||||
if (error)
|
||||
goto fail3;
|
||||
goto fail4;
|
||||
|
||||
error = xpad_led_probe(xpad);
|
||||
if (error)
|
||||
goto fail3;
|
||||
goto fail5;
|
||||
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(xpad->irq_in, udev,
|
||||
@ -907,34 +917,26 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
|
||||
error = input_register_device(xpad->dev);
|
||||
if (error)
|
||||
goto fail4;
|
||||
goto fail6;
|
||||
|
||||
usb_set_intfdata(intf, xpad);
|
||||
|
||||
/*
|
||||
* Submit the int URB immediatly rather than waiting for open
|
||||
* because we get status messages from the device whether
|
||||
* or not any controllers are attached. In fact, it's
|
||||
* exactly the message that a controller has arrived that
|
||||
* we're waiting for.
|
||||
*/
|
||||
if (xpad->xtype == XTYPE_XBOX360W) {
|
||||
xpad->irq_in->dev = xpad->udev;
|
||||
error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
|
||||
if (error)
|
||||
goto fail4;
|
||||
|
||||
/*
|
||||
* Setup the message to set the LEDs on the
|
||||
* controller when it shows up
|
||||
*/
|
||||
xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if(!xpad->bulk_out)
|
||||
goto fail5;
|
||||
if (!xpad->bulk_out) {
|
||||
error = -ENOMEM;
|
||||
goto fail7;
|
||||
}
|
||||
|
||||
xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
|
||||
if(!xpad->bdata)
|
||||
goto fail6;
|
||||
if (!xpad->bdata) {
|
||||
error = -ENOMEM;
|
||||
goto fail8;
|
||||
}
|
||||
|
||||
xpad->bdata[2] = 0x08;
|
||||
switch (intf->cur_altsetting->desc.bInterfaceNumber) {
|
||||
@ -955,14 +957,31 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
usb_fill_bulk_urb(xpad->bulk_out, udev,
|
||||
usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
|
||||
xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
|
||||
|
||||
/*
|
||||
* Submit the int URB immediately rather than waiting for open
|
||||
* because we get status messages from the device whether
|
||||
* or not any controllers are attached. In fact, it's
|
||||
* exactly the message that a controller has arrived that
|
||||
* we're waiting for.
|
||||
*/
|
||||
xpad->irq_in->dev = xpad->udev;
|
||||
error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
|
||||
if (error)
|
||||
goto fail9;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail6: usb_free_urb(xpad->bulk_out);
|
||||
fail5: usb_kill_urb(xpad->irq_in);
|
||||
fail4: usb_free_urb(xpad->irq_in);
|
||||
fail3: xpad_deinit_output(xpad);
|
||||
fail9: kfree(xpad->bdata);
|
||||
fail8: usb_free_urb(xpad->bulk_out);
|
||||
fail7: input_unregister_device(input_dev);
|
||||
input_dev = NULL;
|
||||
fail6: xpad_led_disconnect(xpad);
|
||||
fail5: if (input_dev)
|
||||
input_ff_destroy(input_dev);
|
||||
fail4: xpad_deinit_output(xpad);
|
||||
fail3: usb_free_urb(xpad->irq_in);
|
||||
fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(xpad);
|
||||
@ -974,21 +993,24 @@ static void xpad_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_xpad *xpad = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (xpad) {
|
||||
xpad_led_disconnect(xpad);
|
||||
input_unregister_device(xpad->dev);
|
||||
xpad_deinit_output(xpad);
|
||||
if (xpad->xtype == XTYPE_XBOX360W) {
|
||||
usb_kill_urb(xpad->bulk_out);
|
||||
usb_free_urb(xpad->bulk_out);
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
}
|
||||
usb_free_urb(xpad->irq_in);
|
||||
usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
|
||||
xpad->idata, xpad->idata_dma);
|
||||
kfree(xpad);
|
||||
xpad_led_disconnect(xpad);
|
||||
input_unregister_device(xpad->dev);
|
||||
xpad_deinit_output(xpad);
|
||||
|
||||
if (xpad->xtype == XTYPE_XBOX360W) {
|
||||
usb_kill_urb(xpad->bulk_out);
|
||||
usb_free_urb(xpad->bulk_out);
|
||||
usb_kill_urb(xpad->irq_in);
|
||||
}
|
||||
|
||||
usb_free_urb(xpad->irq_in);
|
||||
usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
|
||||
xpad->idata, xpad->idata_dma);
|
||||
|
||||
kfree(xpad->bdata);
|
||||
kfree(xpad);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
static struct usb_driver xpad_driver = {
|
||||
@ -1000,10 +1022,7 @@ static struct usb_driver xpad_driver = {
|
||||
|
||||
static int __init usb_xpad_init(void)
|
||||
{
|
||||
int result = usb_register(&xpad_driver);
|
||||
if (result == 0)
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
|
||||
return result;
|
||||
return usb_register(&xpad_driver);
|
||||
}
|
||||
|
||||
static void __exit usb_xpad_exit(void)
|
||||
|
@ -196,20 +196,22 @@ config KEYBOARD_GPIO_POLLED
|
||||
module will be called gpio_keys_polled.
|
||||
|
||||
config KEYBOARD_TCA6416
|
||||
tristate "TCA6416 Keypad Support"
|
||||
tristate "TCA6416/TCA6408A Keypad Support"
|
||||
depends on I2C
|
||||
help
|
||||
This driver implements basic keypad functionality
|
||||
for keys connected through TCA6416 IO expander
|
||||
for keys connected through TCA6416/TCA6408A IO expanders.
|
||||
|
||||
Say Y here if your device has keys connected to
|
||||
TCA6416 IO expander. Your board-specific setup logic
|
||||
TCA6416/TCA6408A IO expander. Your board-specific setup logic
|
||||
must also provide pin-mask details(of which TCA6416 pins
|
||||
are used for keypad).
|
||||
|
||||
If enabled the complete TCA6416 device will be managed through
|
||||
If enabled the entire TCA6416 device will be managed through
|
||||
this driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tca6416_keypad.
|
||||
|
||||
config KEYBOARD_MATRIX
|
||||
tristate "GPIO driven matrix keypad support"
|
||||
@ -459,6 +461,15 @@ config KEYBOARD_OMAP4
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap4-keypad.
|
||||
|
||||
config KEYBOARD_SPEAR
|
||||
tristate "ST SPEAR keyboard support"
|
||||
depends on PLAT_SPEAR
|
||||
help
|
||||
Say Y here if you want to use the SPEAR keyboard.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called spear-keboard.
|
||||
|
||||
config KEYBOARD_TC3589X
|
||||
tristate "TC3589X Keypad support"
|
||||
depends on MFD_TC3589X
|
||||
|
@ -38,6 +38,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
|
||||
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
|
||||
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
|
||||
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
||||
|
344
drivers/input/keyboard/spear-keyboard.c
Normal file
344
drivers/input/keyboard/spear-keyboard.c
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* SPEAr Keyboard Driver
|
||||
* Based on omap-keypad driver
|
||||
*
|
||||
* Copyright (C) 2010 ST Microelectronics
|
||||
* Rajeev Kumar<rajeev-dlh.kumar@st.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <plat/keyboard.h>
|
||||
|
||||
/* Keyboard Registers */
|
||||
#define MODE_REG 0x00 /* 16 bit reg */
|
||||
#define STATUS_REG 0x0C /* 2 bit reg */
|
||||
#define DATA_REG 0x10 /* 8 bit reg */
|
||||
#define INTR_MASK 0x54
|
||||
|
||||
/* Register Values */
|
||||
/*
|
||||
* pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode
|
||||
* control register as 1010010(82MHZ)
|
||||
*/
|
||||
#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */
|
||||
#define START_SCAN 0x0100
|
||||
#define SCAN_RATE_10 0x0000
|
||||
#define SCAN_RATE_20 0x0004
|
||||
#define SCAN_RATE_40 0x0008
|
||||
#define SCAN_RATE_80 0x000C
|
||||
#define MODE_KEYBOARD 0x0002
|
||||
#define DATA_AVAIL 0x2
|
||||
|
||||
#define KEY_MASK 0xFF000000
|
||||
#define KEY_VALUE 0x00FFFFFF
|
||||
#define ROW_MASK 0xF0
|
||||
#define COLUMN_MASK 0x0F
|
||||
#define ROW_SHIFT 4
|
||||
|
||||
struct spear_kbd {
|
||||
struct input_dev *input;
|
||||
struct resource *res;
|
||||
void __iomem *io_base;
|
||||
struct clk *clk;
|
||||
unsigned int irq;
|
||||
unsigned short last_key;
|
||||
unsigned short keycodes[256];
|
||||
};
|
||||
|
||||
static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct spear_kbd *kbd = dev_id;
|
||||
struct input_dev *input = kbd->input;
|
||||
unsigned int key;
|
||||
u8 sts, val;
|
||||
|
||||
sts = readb(kbd->io_base + STATUS_REG);
|
||||
if (sts & DATA_AVAIL)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (kbd->last_key != KEY_RESERVED) {
|
||||
input_report_key(input, kbd->last_key, 0);
|
||||
kbd->last_key = KEY_RESERVED;
|
||||
}
|
||||
|
||||
/* following reads active (row, col) pair */
|
||||
val = readb(kbd->io_base + DATA_REG);
|
||||
key = kbd->keycodes[val];
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, val);
|
||||
input_report_key(input, key, 1);
|
||||
input_sync(input);
|
||||
|
||||
kbd->last_key = key;
|
||||
|
||||
/* clear interrupt */
|
||||
writeb(0, kbd->io_base + STATUS_REG);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int spear_kbd_open(struct input_dev *dev)
|
||||
{
|
||||
struct spear_kbd *kbd = input_get_drvdata(dev);
|
||||
int error;
|
||||
u16 val;
|
||||
|
||||
kbd->last_key = KEY_RESERVED;
|
||||
|
||||
error = clk_enable(kbd->clk);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* program keyboard */
|
||||
val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK;
|
||||
writew(val, kbd->io_base + MODE_REG);
|
||||
writeb(1, kbd->io_base + STATUS_REG);
|
||||
|
||||
/* start key scan */
|
||||
val = readw(kbd->io_base + MODE_REG);
|
||||
val |= START_SCAN;
|
||||
writew(val, kbd->io_base + MODE_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spear_kbd_close(struct input_dev *dev)
|
||||
{
|
||||
struct spear_kbd *kbd = input_get_drvdata(dev);
|
||||
u16 val;
|
||||
|
||||
/* stop key scan */
|
||||
val = readw(kbd->io_base + MODE_REG);
|
||||
val &= ~START_SCAN;
|
||||
writew(val, kbd->io_base + MODE_REG);
|
||||
|
||||
clk_disable(kbd->clk);
|
||||
|
||||
kbd->last_key = KEY_RESERVED;
|
||||
}
|
||||
|
||||
static int __devinit spear_kbd_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct kbd_platform_data *pdata = pdev->dev.platform_data;
|
||||
const struct matrix_keymap_data *keymap;
|
||||
struct spear_kbd *kbd;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "Invalid platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keymap = pdata->keymap;
|
||||
if (!keymap) {
|
||||
dev_err(&pdev->dev, "no keymap defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no keyboard resource defined\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "not able to get irq for the device\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!kbd || !input_dev) {
|
||||
dev_err(&pdev->dev, "out of memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
kbd->input = input_dev;
|
||||
kbd->irq = irq;
|
||||
kbd->res = request_mem_region(res->start, resource_size(res),
|
||||
pdev->name);
|
||||
if (!kbd->res) {
|
||||
dev_err(&pdev->dev, "keyboard region already claimed\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
kbd->io_base = ioremap(res->start, resource_size(res));
|
||||
if (!kbd->io_base) {
|
||||
dev_err(&pdev->dev, "ioremap failed for kbd_region\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem_region;
|
||||
}
|
||||
|
||||
kbd->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(kbd->clk)) {
|
||||
error = PTR_ERR(kbd->clk);
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
input_dev->name = "Spear Keyboard";
|
||||
input_dev->phys = "keyboard/input0";
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->open = spear_kbd_open;
|
||||
input_dev->close = spear_kbd_close;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_dev->keycode = kbd->keycodes;
|
||||
input_dev->keycodesize = sizeof(kbd->keycodes[0]);
|
||||
input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes);
|
||||
|
||||
matrix_keypad_build_keymap(keymap, ROW_SHIFT,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
|
||||
input_set_drvdata(input_dev, kbd);
|
||||
|
||||
error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "request_irq fail\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Unable to register keyboard device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
platform_set_drvdata(pdev, kbd);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(kbd->irq, kbd);
|
||||
err_put_clk:
|
||||
clk_put(kbd->clk);
|
||||
err_iounmap:
|
||||
iounmap(kbd->io_base);
|
||||
err_release_mem_region:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(kbd);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit spear_kbd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spear_kbd *kbd = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(kbd->irq, kbd);
|
||||
input_unregister_device(kbd->input);
|
||||
clk_put(kbd->clk);
|
||||
iounmap(kbd->io_base);
|
||||
release_mem_region(kbd->res->start, resource_size(kbd->res));
|
||||
kfree(kbd);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int spear_kbd_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct spear_kbd *kbd = platform_get_drvdata(pdev);
|
||||
struct input_dev *input_dev = kbd->input;
|
||||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
if (input_dev->users)
|
||||
clk_enable(kbd->clk);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
enable_irq_wake(kbd->irq);
|
||||
|
||||
mutex_unlock(&input_dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear_kbd_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct spear_kbd *kbd = platform_get_drvdata(pdev);
|
||||
struct input_dev *input_dev = kbd->input;
|
||||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
disable_irq_wake(kbd->irq);
|
||||
|
||||
if (input_dev->users)
|
||||
clk_enable(kbd->clk);
|
||||
|
||||
mutex_unlock(&input_dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops spear_kbd_pm_ops = {
|
||||
.suspend = spear_kbd_suspend,
|
||||
.resume = spear_kbd_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver spear_kbd_driver = {
|
||||
.probe = spear_kbd_probe,
|
||||
.remove = __devexit_p(spear_kbd_remove),
|
||||
.driver = {
|
||||
.name = "keyboard",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &spear_kbd_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init spear_kbd_init(void)
|
||||
{
|
||||
return platform_driver_register(&spear_kbd_driver);
|
||||
}
|
||||
module_init(spear_kbd_init);
|
||||
|
||||
static void __exit spear_kbd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&spear_kbd_driver);
|
||||
}
|
||||
module_exit(spear_kbd_exit);
|
||||
|
||||
MODULE_AUTHOR("Rajeev Kumar");
|
||||
MODULE_DESCRIPTION("SPEAr Keyboard Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -29,6 +29,7 @@
|
||||
|
||||
static const struct i2c_device_id tca6416_id[] = {
|
||||
{ "tca6416-keys", 16, },
|
||||
{ "tca6408-keys", 8, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tca6416_id);
|
||||
@ -46,8 +47,9 @@ struct tca6416_keypad_chip {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct delayed_work dwork;
|
||||
u16 pinmask;
|
||||
int io_size;
|
||||
int irqnum;
|
||||
u16 pinmask;
|
||||
bool use_polling;
|
||||
struct tca6416_button buttons[0];
|
||||
};
|
||||
@ -56,7 +58,9 @@ static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = i2c_smbus_write_word_data(chip->client, reg << 1, val);
|
||||
error = chip->io_size > 8 ?
|
||||
i2c_smbus_write_word_data(chip->client, reg << 1, val) :
|
||||
i2c_smbus_write_byte_data(chip->client, reg, val);
|
||||
if (error < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"%s failed, reg: %d, val: %d, error: %d\n",
|
||||
@ -71,7 +75,9 @@ static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = i2c_smbus_read_word_data(chip->client, reg << 1);
|
||||
retval = chip->io_size > 8 ?
|
||||
i2c_smbus_read_word_data(chip->client, reg << 1) :
|
||||
i2c_smbus_read_byte_data(chip->client, reg);
|
||||
if (retval < 0) {
|
||||
dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
|
||||
__func__, reg, retval);
|
||||
@ -224,6 +230,7 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client,
|
||||
|
||||
chip->client = client;
|
||||
chip->input = input;
|
||||
chip->io_size = id->driver_data;
|
||||
chip->pinmask = pdata->pinmask;
|
||||
chip->use_polling = pdata->use_polling;
|
||||
|
||||
|
@ -430,4 +430,28 @@ config INPUT_ADXL34X_SPI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adxl34x-spi.
|
||||
|
||||
config INPUT_CMA3000
|
||||
tristate "VTI CMA3000 Tri-axis accelerometer"
|
||||
help
|
||||
Say Y here if you want to use VTI CMA3000_D0x Accelerometer
|
||||
driver
|
||||
|
||||
This driver currently only supports I2C interface to the
|
||||
controller. Also select the I2C method.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cma3000_d0x.
|
||||
|
||||
config INPUT_CMA3000_I2C
|
||||
tristate "Support I2C bus connection"
|
||||
depends on INPUT_CMA3000 && I2C
|
||||
help
|
||||
Say Y here if you want to use VTI CMA3000_D0x Accelerometer
|
||||
through I2C interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cma3000_d0x_i2c.
|
||||
|
||||
endif
|
||||
|
@ -18,6 +18,8 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
|
||||
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
|
||||
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
|
||||
obj-$(CONFIG_INPUT_CM109) += cm109.o
|
||||
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
|
||||
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
|
||||
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
|
||||
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
|
||||
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
|
||||
|
398
drivers/input/misc/cma3000_d0x.c
Normal file
398
drivers/input/misc/cma3000_d0x.c
Normal file
@ -0,0 +1,398 @@
|
||||
/*
|
||||
* VTI CMA3000_D0x Accelerometer driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Hemanth V <hemanthv@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/cma3000.h>
|
||||
|
||||
#include "cma3000_d0x.h"
|
||||
|
||||
#define CMA3000_WHOAMI 0x00
|
||||
#define CMA3000_REVID 0x01
|
||||
#define CMA3000_CTRL 0x02
|
||||
#define CMA3000_STATUS 0x03
|
||||
#define CMA3000_RSTR 0x04
|
||||
#define CMA3000_INTSTATUS 0x05
|
||||
#define CMA3000_DOUTX 0x06
|
||||
#define CMA3000_DOUTY 0x07
|
||||
#define CMA3000_DOUTZ 0x08
|
||||
#define CMA3000_MDTHR 0x09
|
||||
#define CMA3000_MDFFTMR 0x0A
|
||||
#define CMA3000_FFTHR 0x0B
|
||||
|
||||
#define CMA3000_RANGE2G (1 << 7)
|
||||
#define CMA3000_RANGE8G (0 << 7)
|
||||
#define CMA3000_BUSI2C (0 << 4)
|
||||
#define CMA3000_MODEMASK (7 << 1)
|
||||
#define CMA3000_GRANGEMASK (1 << 7)
|
||||
|
||||
#define CMA3000_STATUS_PERR 1
|
||||
#define CMA3000_INTSTATUS_FFDET (1 << 2)
|
||||
|
||||
/* Settling time delay in ms */
|
||||
#define CMA3000_SETDELAY 30
|
||||
|
||||
/* Delay for clearing interrupt in us */
|
||||
#define CMA3000_INTDELAY 44
|
||||
|
||||
|
||||
/*
|
||||
* Bit weights in mg for bit 0, other bits need
|
||||
* multipy factor 2^n. Eight bit is the sign bit.
|
||||
*/
|
||||
#define BIT_TO_2G 18
|
||||
#define BIT_TO_8G 71
|
||||
|
||||
struct cma3000_accl_data {
|
||||
const struct cma3000_bus_ops *bus_ops;
|
||||
const struct cma3000_platform_data *pdata;
|
||||
|
||||
struct device *dev;
|
||||
struct input_dev *input_dev;
|
||||
|
||||
int bit_to_mg;
|
||||
int irq;
|
||||
|
||||
int g_range;
|
||||
u8 mode;
|
||||
|
||||
struct mutex mutex;
|
||||
bool opened;
|
||||
bool suspended;
|
||||
};
|
||||
|
||||
#define CMA3000_READ(data, reg, msg) \
|
||||
(data->bus_ops->read(data->dev, reg, msg))
|
||||
#define CMA3000_SET(data, reg, val, msg) \
|
||||
((data)->bus_ops->write(data->dev, reg, val, msg))
|
||||
|
||||
/*
|
||||
* Conversion for each of the eight modes to g, depending
|
||||
* on G range i.e 2G or 8G. Some modes always operate in
|
||||
* 8G.
|
||||
*/
|
||||
|
||||
static int mode_to_mg[8][2] = {
|
||||
{ 0, 0 },
|
||||
{ BIT_TO_8G, BIT_TO_2G },
|
||||
{ BIT_TO_8G, BIT_TO_2G },
|
||||
{ BIT_TO_8G, BIT_TO_8G },
|
||||
{ BIT_TO_8G, BIT_TO_8G },
|
||||
{ BIT_TO_8G, BIT_TO_2G },
|
||||
{ BIT_TO_8G, BIT_TO_2G },
|
||||
{ 0, 0},
|
||||
};
|
||||
|
||||
static void decode_mg(struct cma3000_accl_data *data, int *datax,
|
||||
int *datay, int *dataz)
|
||||
{
|
||||
/* Data in 2's complement, convert to mg */
|
||||
*datax = ((s8)*datax) * data->bit_to_mg;
|
||||
*datay = ((s8)*datay) * data->bit_to_mg;
|
||||
*dataz = ((s8)*dataz) * data->bit_to_mg;
|
||||
}
|
||||
|
||||
static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct cma3000_accl_data *data = dev_id;
|
||||
int datax, datay, dataz;
|
||||
u8 ctrl, mode, range, intr_status;
|
||||
|
||||
intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
|
||||
if (intr_status < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Check if free fall is detected, report immediately */
|
||||
if (intr_status & CMA3000_INTSTATUS_FFDET) {
|
||||
input_report_abs(data->input_dev, ABS_MISC, 1);
|
||||
input_sync(data->input_dev);
|
||||
} else {
|
||||
input_report_abs(data->input_dev, ABS_MISC, 0);
|
||||
}
|
||||
|
||||
datax = CMA3000_READ(data, CMA3000_DOUTX, "X");
|
||||
datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");
|
||||
dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");
|
||||
|
||||
ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");
|
||||
mode = (ctrl & CMA3000_MODEMASK) >> 1;
|
||||
range = (ctrl & CMA3000_GRANGEMASK) >> 7;
|
||||
|
||||
data->bit_to_mg = mode_to_mg[mode][range];
|
||||
|
||||
/* Interrupt not for this device */
|
||||
if (data->bit_to_mg == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Decode register values to milli g */
|
||||
decode_mg(data, &datax, &datay, &dataz);
|
||||
|
||||
input_report_abs(data->input_dev, ABS_X, datax);
|
||||
input_report_abs(data->input_dev, ABS_Y, datay);
|
||||
input_report_abs(data->input_dev, ABS_Z, dataz);
|
||||
input_sync(data->input_dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cma3000_reset(struct cma3000_accl_data *data)
|
||||
{
|
||||
int val;
|
||||
|
||||
/* Reset sequence */
|
||||
CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");
|
||||
CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");
|
||||
CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");
|
||||
|
||||
/* Settling time delay */
|
||||
mdelay(10);
|
||||
|
||||
val = CMA3000_READ(data, CMA3000_STATUS, "Status");
|
||||
if (val < 0) {
|
||||
dev_err(data->dev, "Reset failed\n");
|
||||
return val;
|
||||
}
|
||||
|
||||
if (val & CMA3000_STATUS_PERR) {
|
||||
dev_err(data->dev, "Parity Error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cma3000_poweron(struct cma3000_accl_data *data)
|
||||
{
|
||||
const struct cma3000_platform_data *pdata = data->pdata;
|
||||
u8 ctrl = 0;
|
||||
int ret;
|
||||
|
||||
if (data->g_range == CMARANGE_2G) {
|
||||
ctrl = (data->mode << 1) | CMA3000_RANGE2G;
|
||||
} else if (data->g_range == CMARANGE_8G) {
|
||||
ctrl = (data->mode << 1) | CMA3000_RANGE8G;
|
||||
} else {
|
||||
dev_info(data->dev,
|
||||
"Invalid G range specified, assuming 8G\n");
|
||||
ctrl = (data->mode << 1) | CMA3000_RANGE8G;
|
||||
}
|
||||
|
||||
ctrl |= data->bus_ops->ctrl_mod;
|
||||
|
||||
CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,
|
||||
"Motion Detect Threshold");
|
||||
CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,
|
||||
"Time register");
|
||||
CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,
|
||||
"Free fall threshold");
|
||||
ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
||||
msleep(CMA3000_SETDELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cma3000_poweroff(struct cma3000_accl_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");
|
||||
msleep(CMA3000_SETDELAY);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cma3000_open(struct input_dev *input_dev)
|
||||
{
|
||||
struct cma3000_accl_data *data = input_get_drvdata(input_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
if (!data->suspended)
|
||||
cma3000_poweron(data);
|
||||
|
||||
data->opened = true;
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cma3000_close(struct input_dev *input_dev)
|
||||
{
|
||||
struct cma3000_accl_data *data = input_get_drvdata(input_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
if (!data->suspended)
|
||||
cma3000_poweroff(data);
|
||||
|
||||
data->opened = false;
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
}
|
||||
|
||||
void cma3000_suspend(struct cma3000_accl_data *data)
|
||||
{
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
if (!data->suspended && data->opened)
|
||||
cma3000_poweroff(data);
|
||||
|
||||
data->suspended = true;
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(cma3000_suspend);
|
||||
|
||||
|
||||
void cma3000_resume(struct cma3000_accl_data *data)
|
||||
{
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
if (data->suspended && data->opened)
|
||||
cma3000_poweron(data);
|
||||
|
||||
data->suspended = false;
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(cma3000_resume);
|
||||
|
||||
struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
|
||||
const struct cma3000_bus_ops *bops)
|
||||
{
|
||||
const struct cma3000_platform_data *pdata = dev->platform_data;
|
||||
struct cma3000_accl_data *data;
|
||||
struct input_dev *input_dev;
|
||||
int rev;
|
||||
int error;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(dev, "platform data not found\n");
|
||||
error = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
|
||||
/* if no IRQ return error */
|
||||
if (irq == 0) {
|
||||
error = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!data || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
data->input_dev = input_dev;
|
||||
data->bus_ops = bops;
|
||||
data->pdata = pdata;
|
||||
data->irq = irq;
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
data->mode = pdata->mode;
|
||||
if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) {
|
||||
data->mode = CMAMODE_MOTDET;
|
||||
dev_warn(dev,
|
||||
"Invalid mode specified, assuming Motion Detect\n");
|
||||
}
|
||||
|
||||
data->g_range = pdata->g_range;
|
||||
if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {
|
||||
dev_info(dev,
|
||||
"Invalid G range specified, assuming 8G\n");
|
||||
data->g_range = CMARANGE_8G;
|
||||
}
|
||||
|
||||
input_dev->name = "cma3000-accelerometer";
|
||||
input_dev->id.bustype = bops->bustype;
|
||||
input_dev->open = cma3000_open;
|
||||
input_dev->close = cma3000_close;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
-data->g_range, data->g_range, pdata->fuzz_x, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
-data->g_range, data->g_range, pdata->fuzz_y, 0);
|
||||
input_set_abs_params(input_dev, ABS_Z,
|
||||
-data->g_range, data->g_range, pdata->fuzz_z, 0);
|
||||
input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);
|
||||
|
||||
input_set_drvdata(input_dev, data);
|
||||
|
||||
error = cma3000_reset(data);
|
||||
if (error)
|
||||
goto err_free_mem;
|
||||
|
||||
rev = CMA3000_READ(data, CMA3000_REVID, "Revid");
|
||||
if (rev < 0) {
|
||||
error = rev;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
pr_info("CMA3000 Accelerometer: Revision %x\n", rev);
|
||||
|
||||
error = request_threaded_irq(irq, NULL, cma3000_thread_irq,
|
||||
pdata->irqflags | IRQF_ONESHOT,
|
||||
"cma3000_d0x", data);
|
||||
if (error) {
|
||||
dev_err(dev, "request_threaded_irq failed\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(data->input_dev);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(irq, data);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(data);
|
||||
err_out:
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
EXPORT_SYMBOL(cma3000_init);
|
||||
|
||||
void cma3000_exit(struct cma3000_accl_data *data)
|
||||
{
|
||||
free_irq(data->irq, data);
|
||||
input_unregister_device(data->input_dev);
|
||||
kfree(data);
|
||||
}
|
||||
EXPORT_SYMBOL(cma3000_exit);
|
||||
|
||||
MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
|
42
drivers/input/misc/cma3000_d0x.h
Normal file
42
drivers/input/misc/cma3000_d0x.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* VTI CMA3000_D0x Accelerometer driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Hemanth V <hemanthv@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _INPUT_CMA3000_H
|
||||
#define _INPUT_CMA3000_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
struct device;
|
||||
struct cma3000_accl_data;
|
||||
|
||||
struct cma3000_bus_ops {
|
||||
u16 bustype;
|
||||
u8 ctrl_mod;
|
||||
int (*read)(struct device *, u8, char *);
|
||||
int (*write)(struct device *, u8, u8, char *);
|
||||
};
|
||||
|
||||
struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
|
||||
const struct cma3000_bus_ops *bops);
|
||||
void cma3000_exit(struct cma3000_accl_data *);
|
||||
void cma3000_suspend(struct cma3000_accl_data *);
|
||||
void cma3000_resume(struct cma3000_accl_data *);
|
||||
|
||||
#endif
|
143
drivers/input/misc/cma3000_d0x_i2c.c
Normal file
143
drivers/input/misc/cma3000_d0x_i2c.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Implements I2C interface for VTI CMA300_D0x Accelerometer driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Hemanth V <hemanthv@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input/cma3000.h>
|
||||
#include "cma3000_d0x.h"
|
||||
|
||||
static int cma3000_i2c_set(struct device *dev,
|
||||
u8 reg, u8 val, char *msg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, reg, val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev,
|
||||
"%s failed (%s, %d)\n", __func__, msg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cma3000_i2c_read(struct device *dev, u8 reg, char *msg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, reg);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev,
|
||||
"%s failed (%s, %d)\n", __func__, msg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct cma3000_bus_ops cma3000_i2c_bops = {
|
||||
.bustype = BUS_I2C,
|
||||
#define CMA3000_BUSI2C (0 << 4)
|
||||
.ctrl_mod = CMA3000_BUSI2C,
|
||||
.read = cma3000_i2c_read,
|
||||
.write = cma3000_i2c_set,
|
||||
};
|
||||
|
||||
static int __devinit cma3000_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cma3000_accl_data *data;
|
||||
|
||||
data = cma3000_init(&client->dev, client->irq, &cma3000_i2c_bops);
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit cma3000_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cma3000_accl_data *data = i2c_get_clientdata(client);
|
||||
|
||||
cma3000_exit(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cma3000_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cma3000_accl_data *data = i2c_get_clientdata(client);
|
||||
|
||||
cma3000_suspend(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cma3000_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cma3000_accl_data *data = i2c_get_clientdata(client);
|
||||
|
||||
cma3000_resume(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cma3000_i2c_pm_ops = {
|
||||
.suspend = cma3000_i2c_suspend,
|
||||
.resume = cma3000_i2c_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id cma3000_i2c_id[] = {
|
||||
{ "cma3000_d01", 0 },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id);
|
||||
|
||||
static struct i2c_driver cma3000_i2c_driver = {
|
||||
.probe = cma3000_i2c_probe,
|
||||
.remove = __devexit_p(cma3000_i2c_remove),
|
||||
.id_table = cma3000_i2c_id,
|
||||
.driver = {
|
||||
.name = "cma3000_i2c_accl",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &cma3000_i2c_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init cma3000_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&cma3000_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit cma3000_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&cma3000_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(cma3000_i2c_init);
|
||||
module_exit(cma3000_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
|
@ -169,19 +169,29 @@ static int __devexit pcf8574_kp_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pcf8574_kp_resume(struct i2c_client *client)
|
||||
static int pcf8574_kp_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf8574_kp_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int pcf8574_kp_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops pcf8574_kp_pm_ops = {
|
||||
.suspend = pcf8574_kp_suspend,
|
||||
.resume = pcf8574_kp_resume,
|
||||
};
|
||||
|
||||
#else
|
||||
# define pcf8574_kp_resume NULL
|
||||
# define pcf8574_kp_suspend NULL
|
||||
@ -197,11 +207,12 @@ static struct i2c_driver pcf8574_kp_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &pcf8574_kp_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = pcf8574_kp_probe,
|
||||
.remove = __devexit_p(pcf8574_kp_remove),
|
||||
.suspend = pcf8574_kp_suspend,
|
||||
.resume = pcf8574_kp_resume,
|
||||
.id_table = pcf8574_kp_id,
|
||||
};
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/uinput.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include "../input-compat.h"
|
||||
|
||||
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
||||
@ -406,8 +407,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
|
||||
goto exit;
|
||||
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
|
||||
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
|
||||
input_mt_create_slots(dev, nslot);
|
||||
input_set_events_per_packet(dev, 6 * nslot);
|
||||
input_mt_init_slots(dev, nslot);
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
input_set_events_per_packet(dev, 60);
|
||||
}
|
||||
@ -680,6 +680,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
||||
retval = uinput_set_bit(arg, swbit, SW_MAX);
|
||||
break;
|
||||
|
||||
case UI_SET_PROPBIT:
|
||||
retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
|
||||
break;
|
||||
|
||||
case UI_SET_PHYS:
|
||||
if (udev->state == UIST_CREATED) {
|
||||
retval = -EINVAL;
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include "psmouse.h"
|
||||
#include "hgpk.h"
|
||||
|
||||
#define ILLEGAL_XY 999999
|
||||
|
||||
static bool tpdebug;
|
||||
module_param(tpdebug, bool, 0644);
|
||||
MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
|
||||
@ -47,48 +49,150 @@ MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
|
||||
static int recalib_delta = 100;
|
||||
module_param(recalib_delta, int, 0644);
|
||||
MODULE_PARM_DESC(recalib_delta,
|
||||
"packets containing a delta this large will cause a recalibration.");
|
||||
"packets containing a delta this large will be discarded, and a "
|
||||
"recalibration may be scheduled.");
|
||||
|
||||
static int jumpy_delay = 1000;
|
||||
static int jumpy_delay = 20;
|
||||
module_param(jumpy_delay, int, 0644);
|
||||
MODULE_PARM_DESC(jumpy_delay,
|
||||
"delay (ms) before recal after jumpiness detected");
|
||||
|
||||
static int spew_delay = 1000;
|
||||
static int spew_delay = 1;
|
||||
module_param(spew_delay, int, 0644);
|
||||
MODULE_PARM_DESC(spew_delay,
|
||||
"delay (ms) before recal after packet spew detected");
|
||||
|
||||
static int recal_guard_time = 2000;
|
||||
static int recal_guard_time;
|
||||
module_param(recal_guard_time, int, 0644);
|
||||
MODULE_PARM_DESC(recal_guard_time,
|
||||
"interval (ms) during which recal will be restarted if packet received");
|
||||
|
||||
static int post_interrupt_delay = 1000;
|
||||
static int post_interrupt_delay = 40;
|
||||
module_param(post_interrupt_delay, int, 0644);
|
||||
MODULE_PARM_DESC(post_interrupt_delay,
|
||||
"delay (ms) before recal after recal interrupt detected");
|
||||
|
||||
static bool autorecal = true;
|
||||
module_param(autorecal, bool, 0644);
|
||||
MODULE_PARM_DESC(autorecal, "enable recalibration in the driver");
|
||||
|
||||
static char hgpk_mode_name[16];
|
||||
module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644);
|
||||
MODULE_PARM_DESC(hgpk_mode,
|
||||
"default hgpk mode: mouse, glidesensor or pentablet");
|
||||
|
||||
static int hgpk_default_mode = HGPK_MODE_MOUSE;
|
||||
|
||||
static const char * const hgpk_mode_names[] = {
|
||||
[HGPK_MODE_MOUSE] = "Mouse",
|
||||
[HGPK_MODE_GLIDESENSOR] = "GlideSensor",
|
||||
[HGPK_MODE_PENTABLET] = "PenTablet",
|
||||
};
|
||||
|
||||
static int hgpk_mode_from_name(const char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) {
|
||||
const char *name = hgpk_mode_names[i];
|
||||
if (strlen(name) == len && !strncasecmp(name, buf, len))
|
||||
return i;
|
||||
}
|
||||
|
||||
return HGPK_MODE_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
|
||||
* above the pad and still have it send packets. This causes a jump cursor
|
||||
* when one places their finger on the pad. We can probably detect the
|
||||
* jump as we see a large deltas (>= 100px). In mouse mode, I've been
|
||||
* unable to even come close to 100px deltas during normal usage, so I think
|
||||
* this threshold is safe. If a large delta occurs, trigger a recalibration.
|
||||
* see if new value is within 20% of half of old value
|
||||
*/
|
||||
static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
|
||||
static int approx_half(int curr, int prev)
|
||||
{
|
||||
int belowhalf, abovehalf;
|
||||
|
||||
if (curr < 5 || prev < 5)
|
||||
return 0;
|
||||
|
||||
belowhalf = (prev * 8) / 20;
|
||||
abovehalf = (prev * 12) / 20;
|
||||
|
||||
return belowhalf < curr && curr <= abovehalf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Throw out oddly large delta packets, and any that immediately follow whose
|
||||
* values are each approximately half of the previous. It seems that the ALPS
|
||||
* firmware emits errant packets, and they get averaged out slowly.
|
||||
*/
|
||||
static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
int avx, avy;
|
||||
bool do_recal = false;
|
||||
|
||||
if (abs(x) > recalib_delta || abs(y) > recalib_delta) {
|
||||
hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n",
|
||||
recalib_delta, x, y);
|
||||
/* My car gets forty rods to the hogshead and that's the
|
||||
* way I likes it! */
|
||||
avx = abs(x);
|
||||
avy = abs(y);
|
||||
|
||||
/* discard if too big, or half that but > 4 times the prev delta */
|
||||
if (avx > recalib_delta ||
|
||||
(avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) {
|
||||
hgpk_err(psmouse, "detected %dpx jump in x\n", x);
|
||||
priv->xbigj = avx;
|
||||
} else if (approx_half(avx, priv->xbigj)) {
|
||||
hgpk_err(psmouse, "detected secondary %dpx jump in x\n", x);
|
||||
priv->xbigj = avx;
|
||||
priv->xsaw_secondary++;
|
||||
} else {
|
||||
if (priv->xbigj && priv->xsaw_secondary > 1)
|
||||
do_recal = true;
|
||||
priv->xbigj = 0;
|
||||
priv->xsaw_secondary = 0;
|
||||
}
|
||||
|
||||
if (avy > recalib_delta ||
|
||||
(avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) {
|
||||
hgpk_err(psmouse, "detected %dpx jump in y\n", y);
|
||||
priv->ybigj = avy;
|
||||
} else if (approx_half(avy, priv->ybigj)) {
|
||||
hgpk_err(psmouse, "detected secondary %dpx jump in y\n", y);
|
||||
priv->ybigj = avy;
|
||||
priv->ysaw_secondary++;
|
||||
} else {
|
||||
if (priv->ybigj && priv->ysaw_secondary > 1)
|
||||
do_recal = true;
|
||||
priv->ybigj = 0;
|
||||
priv->ysaw_secondary = 0;
|
||||
}
|
||||
|
||||
priv->xlast = avx;
|
||||
priv->ylast = avy;
|
||||
|
||||
if (do_recal && jumpy_delay) {
|
||||
hgpk_err(psmouse, "scheduling recalibration\n");
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||
msecs_to_jiffies(jumpy_delay));
|
||||
}
|
||||
|
||||
return priv->xbigj || priv->ybigj;
|
||||
}
|
||||
|
||||
static void hgpk_reset_spew_detection(struct hgpk_data *priv)
|
||||
{
|
||||
priv->spew_count = 0;
|
||||
priv->dupe_count = 0;
|
||||
priv->x_tally = 0;
|
||||
priv->y_tally = 0;
|
||||
priv->spew_flag = NO_SPEW;
|
||||
}
|
||||
|
||||
static void hgpk_reset_hack_state(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
priv->abs_x = priv->abs_y = -1;
|
||||
priv->xlast = priv->ylast = ILLEGAL_XY;
|
||||
priv->xbigj = priv->ybigj = 0;
|
||||
priv->xsaw_secondary = priv->ysaw_secondary = 0;
|
||||
hgpk_reset_spew_detection(priv);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -116,20 +220,57 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
|
||||
if (l || r)
|
||||
return;
|
||||
|
||||
/* don't track spew if the workaround feature has been turned off */
|
||||
if (!spew_delay)
|
||||
return;
|
||||
|
||||
if (abs(x) > 3 || abs(y) > 3) {
|
||||
/* no spew, or spew ended */
|
||||
hgpk_reset_spew_detection(priv);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Keep a tally of the overall delta to the cursor position caused by
|
||||
* the spew */
|
||||
priv->x_tally += x;
|
||||
priv->y_tally += y;
|
||||
|
||||
if (++priv->count > 100) {
|
||||
switch (priv->spew_flag) {
|
||||
case NO_SPEW:
|
||||
/* we're not spewing, but this packet might be the start */
|
||||
priv->spew_flag = MAYBE_SPEWING;
|
||||
|
||||
/* fall-through */
|
||||
|
||||
case MAYBE_SPEWING:
|
||||
priv->spew_count++;
|
||||
|
||||
if (priv->spew_count < SPEW_WATCH_COUNT)
|
||||
break;
|
||||
|
||||
/* excessive spew detected, request recalibration */
|
||||
priv->spew_flag = SPEW_DETECTED;
|
||||
|
||||
/* fall-through */
|
||||
|
||||
case SPEW_DETECTED:
|
||||
/* only recalibrate when the overall delta to the cursor
|
||||
* is really small. if the spew is causing significant cursor
|
||||
* movement, it is probably a case of the user moving the
|
||||
* cursor very slowly across the screen. */
|
||||
if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
|
||||
hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
|
||||
hgpk_err(psmouse, "packet spew detected (%d,%d)\n",
|
||||
priv->x_tally, priv->y_tally);
|
||||
priv->spew_flag = RECALIBRATING;
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||
msecs_to_jiffies(spew_delay));
|
||||
}
|
||||
/* reset every 100 packets */
|
||||
priv->count = 0;
|
||||
priv->x_tally = 0;
|
||||
priv->y_tally = 0;
|
||||
|
||||
break;
|
||||
case RECALIBRATING:
|
||||
/* we already detected a spew and requested a recalibration,
|
||||
* just wait for the queue to kick into action. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,25 +284,168 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
|
||||
* swr/swl are the left/right buttons.
|
||||
* x-neg/y-neg are the x and y delta negative bits
|
||||
* x-over/y-over are the x and y overflow bits
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* HGPK Advanced Mode - single-mode format
|
||||
*
|
||||
* byte 0(PT): 1 1 0 0 1 1 1 1
|
||||
* byte 0(GS): 1 1 1 1 1 1 1 1
|
||||
* byte 1: 0 x6 x5 x4 x3 x2 x1 x0
|
||||
* byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0
|
||||
* byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw
|
||||
* byte 3: 0 y9 y8 y7 1 0 swr swl
|
||||
* byte 4: 0 y6 y5 y4 y3 y2 y1 y0
|
||||
* byte 5: 0 z6 z5 z4 z3 z2 z1 z0
|
||||
*
|
||||
* ?'s are not defined in the protocol spec, may vary between models.
|
||||
*
|
||||
* swr/swl are the left/right buttons.
|
||||
*
|
||||
* pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a
|
||||
* pen/finger
|
||||
*/
|
||||
static int hgpk_validate_byte(unsigned char *packet)
|
||||
static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet)
|
||||
{
|
||||
return (packet[0] & 0x0C) != 0x08;
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
int pktcnt = psmouse->pktcnt;
|
||||
bool valid;
|
||||
|
||||
switch (priv->mode) {
|
||||
case HGPK_MODE_MOUSE:
|
||||
valid = (packet[0] & 0x0C) == 0x08;
|
||||
break;
|
||||
|
||||
case HGPK_MODE_GLIDESENSOR:
|
||||
valid = pktcnt == 1 ?
|
||||
packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80);
|
||||
break;
|
||||
|
||||
case HGPK_MODE_PENTABLET:
|
||||
valid = pktcnt == 1 ?
|
||||
packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80);
|
||||
break;
|
||||
|
||||
default:
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
hgpk_dbg(psmouse,
|
||||
"bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n",
|
||||
priv->mode, pktcnt,
|
||||
psmouse->packet[0], psmouse->packet[1],
|
||||
psmouse->packet[2], psmouse->packet[3],
|
||||
psmouse->packet[4], psmouse->packet[5]);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
static void hgpk_process_packet(struct psmouse *psmouse)
|
||||
static void hgpk_process_advanced_packet(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
struct input_dev *idev = psmouse->dev;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
int down = !!(packet[2] & 2);
|
||||
int left = !!(packet[3] & 1);
|
||||
int right = !!(packet[3] & 2);
|
||||
int x = packet[1] | ((packet[2] & 0x78) << 4);
|
||||
int y = packet[4] | ((packet[3] & 0x70) << 3);
|
||||
|
||||
if (priv->mode == HGPK_MODE_GLIDESENSOR) {
|
||||
int pt_down = !!(packet[2] & 1);
|
||||
int finger_down = !!(packet[2] & 2);
|
||||
int z = packet[5];
|
||||
|
||||
input_report_abs(idev, ABS_PRESSURE, z);
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "pd=%d fd=%d z=%d",
|
||||
pt_down, finger_down, z);
|
||||
} else {
|
||||
/*
|
||||
* PenTablet mode does not report pressure, so we don't
|
||||
* report it here
|
||||
*/
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "pd=%d ", down);
|
||||
}
|
||||
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
|
||||
|
||||
input_report_key(idev, BTN_TOUCH, down);
|
||||
input_report_key(idev, BTN_LEFT, left);
|
||||
input_report_key(idev, BTN_RIGHT, right);
|
||||
|
||||
/*
|
||||
* If this packet says that the finger was removed, reset our position
|
||||
* tracking so that we don't erroneously detect a jump on next press.
|
||||
*/
|
||||
if (!down) {
|
||||
hgpk_reset_hack_state(psmouse);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Weed out duplicate packets (we get quite a few, and they mess up
|
||||
* our jump detection)
|
||||
*/
|
||||
if (x == priv->abs_x && y == priv->abs_y) {
|
||||
if (++priv->dupe_count > SPEW_WATCH_COUNT) {
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "hard spew detected\n");
|
||||
priv->spew_flag = RECALIBRATING;
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||
msecs_to_jiffies(spew_delay));
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* not a duplicate, continue with position reporting */
|
||||
priv->dupe_count = 0;
|
||||
|
||||
/* Don't apply hacks in PT mode, it seems reliable */
|
||||
if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) {
|
||||
int x_diff = priv->abs_x - x;
|
||||
int y_diff = priv->abs_y - y;
|
||||
if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) {
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "discarding\n");
|
||||
goto done;
|
||||
}
|
||||
hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff);
|
||||
}
|
||||
|
||||
input_report_abs(idev, ABS_X, x);
|
||||
input_report_abs(idev, ABS_Y, y);
|
||||
priv->abs_x = x;
|
||||
priv->abs_y = y;
|
||||
|
||||
done:
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void hgpk_process_simple_packet(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
int x, y, left, right;
|
||||
int left = packet[0] & 1;
|
||||
int right = (packet[0] >> 1) & 1;
|
||||
int x = packet[1] - ((packet[0] << 4) & 0x100);
|
||||
int y = ((packet[0] << 3) & 0x100) - packet[2];
|
||||
|
||||
left = packet[0] & 1;
|
||||
right = (packet[0] >> 1) & 1;
|
||||
if (packet[0] & 0xc0)
|
||||
hgpk_dbg(psmouse,
|
||||
"overflow -- 0x%02x 0x%02x 0x%02x\n",
|
||||
packet[0], packet[1], packet[2]);
|
||||
|
||||
x = packet[1] - ((packet[0] << 4) & 0x100);
|
||||
y = ((packet[0] << 3) & 0x100) - packet[2];
|
||||
if (hgpk_discard_decay_hack(psmouse, x, y)) {
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "discarding\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hgpk_jumpy_hack(psmouse, x, y);
|
||||
hgpk_spewing_hack(psmouse, left, right, x, y);
|
||||
|
||||
if (tpdebug)
|
||||
@ -180,15 +464,14 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
if (hgpk_validate_byte(psmouse->packet)) {
|
||||
hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n",
|
||||
__func__, psmouse->pktcnt, psmouse->packet[0],
|
||||
psmouse->packet[1], psmouse->packet[2]);
|
||||
if (!hgpk_is_byte_valid(psmouse, psmouse->packet))
|
||||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (psmouse->pktcnt >= psmouse->pktsize) {
|
||||
hgpk_process_packet(psmouse);
|
||||
if (priv->mode == HGPK_MODE_MOUSE)
|
||||
hgpk_process_simple_packet(psmouse);
|
||||
else
|
||||
hgpk_process_advanced_packet(psmouse);
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
@ -210,33 +493,176 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
}
|
||||
|
||||
static int hgpk_select_mode(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
int i;
|
||||
int cmd;
|
||||
|
||||
/*
|
||||
* 4 disables to enable advanced mode
|
||||
* then 3 0xf2 bytes as the preamble for GS/PT selection
|
||||
*/
|
||||
const int advanced_init[] = {
|
||||
PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE,
|
||||
PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE,
|
||||
0xf2, 0xf2, 0xf2,
|
||||
};
|
||||
|
||||
switch (priv->mode) {
|
||||
case HGPK_MODE_MOUSE:
|
||||
psmouse->pktsize = 3;
|
||||
break;
|
||||
|
||||
case HGPK_MODE_GLIDESENSOR:
|
||||
case HGPK_MODE_PENTABLET:
|
||||
psmouse->pktsize = 6;
|
||||
|
||||
/* Switch to 'Advanced mode.', four disables in a row. */
|
||||
for (i = 0; i < ARRAY_SIZE(advanced_init); i++)
|
||||
if (ps2_command(ps2dev, NULL, advanced_init[i]))
|
||||
return -EIO;
|
||||
|
||||
/* select between GlideSensor (mouse) or PenTablet */
|
||||
cmd = priv->mode == HGPK_MODE_GLIDESENSOR ?
|
||||
PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21;
|
||||
|
||||
if (ps2_command(ps2dev, NULL, cmd))
|
||||
return -EIO;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hgpk_setup_input_device(struct input_dev *input,
|
||||
struct input_dev *old_input,
|
||||
enum hgpk_mode mode)
|
||||
{
|
||||
if (old_input) {
|
||||
input->name = old_input->name;
|
||||
input->phys = old_input->phys;
|
||||
input->id = old_input->id;
|
||||
input->dev.parent = old_input->dev.parent;
|
||||
}
|
||||
|
||||
memset(input->evbit, 0, sizeof(input->evbit));
|
||||
memset(input->relbit, 0, sizeof(input->relbit));
|
||||
memset(input->keybit, 0, sizeof(input->keybit));
|
||||
|
||||
/* All modes report left and right buttons */
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(BTN_RIGHT, input->keybit);
|
||||
|
||||
switch (mode) {
|
||||
case HGPK_MODE_MOUSE:
|
||||
__set_bit(EV_REL, input->evbit);
|
||||
__set_bit(REL_X, input->relbit);
|
||||
__set_bit(REL_Y, input->relbit);
|
||||
break;
|
||||
|
||||
case HGPK_MODE_GLIDESENSOR:
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
|
||||
/* GlideSensor has pressure sensor, PenTablet does not */
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0);
|
||||
|
||||
/* From device specs */
|
||||
input_set_abs_params(input, ABS_X, 0, 399, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, 290, 0, 0);
|
||||
|
||||
/* Calculated by hand based on usable size (52mm x 38mm) */
|
||||
input_abs_set_res(input, ABS_X, 8);
|
||||
input_abs_set_res(input, ABS_Y, 8);
|
||||
break;
|
||||
|
||||
case HGPK_MODE_PENTABLET:
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
|
||||
/* From device specs */
|
||||
input_set_abs_params(input, ABS_X, 0, 999, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 5, 239, 0, 0);
|
||||
|
||||
/* Calculated by hand based on usable size (156mm x 38mm) */
|
||||
input_abs_set_res(input, ABS_X, 6);
|
||||
input_abs_set_res(input, ABS_Y, 8);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate)
|
||||
{
|
||||
int err;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
if (recalibrate) {
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
|
||||
/* send the recalibrate request */
|
||||
if (ps2_command(ps2dev, NULL, 0xf5) ||
|
||||
ps2_command(ps2dev, NULL, 0xf5) ||
|
||||
ps2_command(ps2dev, NULL, 0xe6) ||
|
||||
ps2_command(ps2dev, NULL, 0xf5)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* according to ALPS, 150mS is required for recalibration */
|
||||
msleep(150);
|
||||
}
|
||||
|
||||
err = hgpk_select_mode(psmouse);
|
||||
if (err) {
|
||||
hgpk_err(psmouse, "failed to select mode\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
hgpk_reset_hack_state(psmouse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hgpk_force_recalibrate(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
int err;
|
||||
|
||||
/* C-series touchpads added the recalibrate command */
|
||||
if (psmouse->model < HGPK_MODEL_C)
|
||||
return 0;
|
||||
|
||||
if (!autorecal) {
|
||||
hgpk_dbg(psmouse, "recalibrations disabled, ignoring\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hgpk_dbg(psmouse, "recalibrating touchpad..\n");
|
||||
|
||||
/* we don't want to race with the irq handler, nor with resyncs */
|
||||
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
|
||||
|
||||
/* start by resetting the device */
|
||||
psmouse_reset(psmouse);
|
||||
err = hgpk_reset_device(psmouse, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* send the recalibrate request */
|
||||
if (ps2_command(ps2dev, NULL, 0xf5) ||
|
||||
ps2_command(ps2dev, NULL, 0xf5) ||
|
||||
ps2_command(ps2dev, NULL, 0xe6) ||
|
||||
ps2_command(ps2dev, NULL, 0xf5)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* according to ALPS, 150mS is required for recalibration */
|
||||
msleep(150);
|
||||
|
||||
/* XXX: If a finger is down during this delay, recalibration will
|
||||
/*
|
||||
* XXX: If a finger is down during this delay, recalibration will
|
||||
* detect capacitance incorrectly. This is a hardware bug, and
|
||||
* we don't have a good way to deal with it. The 2s window stuff
|
||||
* (below) is our best option for now.
|
||||
@ -247,25 +673,35 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse)
|
||||
|
||||
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
|
||||
|
||||
/* After we recalibrate, we shouldn't get any packets for 2s. If
|
||||
* we do, it's likely that someone's finger was on the touchpad.
|
||||
* If someone's finger *was* on the touchpad, it's probably
|
||||
* miscalibrated. So, we should schedule another recalibration
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "touchpad reactivated\n");
|
||||
|
||||
/*
|
||||
* If we get packets right away after recalibrating, it's likely
|
||||
* that a finger was on the touchpad. If so, it's probably
|
||||
* miscalibrated, so we optionally schedule another.
|
||||
*/
|
||||
priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time);
|
||||
if (recal_guard_time)
|
||||
priv->recalib_window = jiffies +
|
||||
msecs_to_jiffies(recal_guard_time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This kills power to the touchpad; according to ALPS, current consumption
|
||||
* goes down to 50uA after running this. To turn power back on, we drive
|
||||
* MS-DAT low.
|
||||
* This puts the touchpad in a power saving mode; according to ALPS, current
|
||||
* consumption goes down to 50uA after running this. To turn power back on,
|
||||
* we drive MS-DAT low. Measuring with a 1mA resolution ammeter says that
|
||||
* the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this.
|
||||
*
|
||||
* We have no formal spec that details this operation -- the low-power
|
||||
* sequence came from a long-lost email trail.
|
||||
*/
|
||||
static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
|
||||
static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int timeo;
|
||||
int err;
|
||||
|
||||
/* Added on D-series touchpads */
|
||||
if (psmouse->model < HGPK_MODEL_D)
|
||||
@ -279,24 +715,27 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
|
||||
* the controller. Once we get an ACK back from it, it
|
||||
* means we can continue with the touchpad re-init. ALPS
|
||||
* tells us that 1s should be long enough, so set that as
|
||||
* the upper bound.
|
||||
* the upper bound. (in practice, it takes about 3 loops.)
|
||||
*/
|
||||
for (timeo = 20; timeo > 0; timeo--) {
|
||||
if (!ps2_sendbyte(&psmouse->ps2dev,
|
||||
PSMOUSE_CMD_DISABLE, 20))
|
||||
break;
|
||||
msleep(50);
|
||||
msleep(25);
|
||||
}
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
err = hgpk_reset_device(psmouse, false);
|
||||
if (err) {
|
||||
hgpk_err(psmouse, "Failed to reset device!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* should be all set, enable the touchpad */
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
|
||||
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
|
||||
|
||||
hgpk_dbg(psmouse, "Touchpad powered up.\n");
|
||||
} else {
|
||||
hgpk_dbg(psmouse, "Powering off touchpad.\n");
|
||||
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
|
||||
|
||||
if (ps2_command(ps2dev, NULL, 0xec) ||
|
||||
ps2_command(ps2dev, NULL, 0xec) ||
|
||||
@ -304,6 +743,8 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
|
||||
return -1;
|
||||
}
|
||||
|
||||
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
|
||||
|
||||
/* probably won't see an ACK, the touchpad will be off */
|
||||
ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);
|
||||
}
|
||||
@ -319,17 +760,20 @@ static int hgpk_poll(struct psmouse *psmouse)
|
||||
|
||||
static int hgpk_reconnect(struct psmouse *psmouse)
|
||||
{
|
||||
/* During suspend/resume the ps2 rails remain powered. We don't want
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
/*
|
||||
* During suspend/resume the ps2 rails remain powered. We don't want
|
||||
* to do a reset because it's flush data out of buffers; however,
|
||||
* earlier prototypes (B1) had some brokenness that required a reset. */
|
||||
* earlier prototypes (B1) had some brokenness that required a reset.
|
||||
*/
|
||||
if (olpc_board_at_least(olpc_board(0xb2)))
|
||||
if (psmouse->ps2dev.serio->dev.power.power_state.event !=
|
||||
PM_EVENT_ON)
|
||||
return 0;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
return 0;
|
||||
priv->powered = 1;
|
||||
return hgpk_reset_device(psmouse, false);
|
||||
}
|
||||
|
||||
static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf)
|
||||
@ -355,7 +799,7 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
|
||||
* hgpk_toggle_power will deal w/ state so
|
||||
* we're not racing w/ irq
|
||||
*/
|
||||
err = hgpk_toggle_power(psmouse, value);
|
||||
err = hgpk_toggle_powersave(psmouse, value);
|
||||
if (!err)
|
||||
priv->powered = value;
|
||||
}
|
||||
@ -366,6 +810,65 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
|
||||
__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
|
||||
hgpk_show_powered, hgpk_set_powered, false);
|
||||
|
||||
static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]);
|
||||
}
|
||||
|
||||
static ssize_t attr_set_mode(struct psmouse *psmouse, void *data,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
enum hgpk_mode old_mode = priv->mode;
|
||||
enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len);
|
||||
struct input_dev *old_dev = psmouse->dev;
|
||||
struct input_dev *new_dev;
|
||||
int err;
|
||||
|
||||
if (new_mode == HGPK_MODE_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
if (old_mode == new_mode)
|
||||
return len;
|
||||
|
||||
new_dev = input_allocate_device();
|
||||
if (!new_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
|
||||
|
||||
/* Switch device into the new mode */
|
||||
priv->mode = new_mode;
|
||||
err = hgpk_reset_device(psmouse, false);
|
||||
if (err)
|
||||
goto err_try_restore;
|
||||
|
||||
hgpk_setup_input_device(new_dev, old_dev, new_mode);
|
||||
|
||||
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
|
||||
|
||||
err = input_register_device(new_dev);
|
||||
if (err)
|
||||
goto err_try_restore;
|
||||
|
||||
psmouse->dev = new_dev;
|
||||
input_unregister_device(old_dev);
|
||||
|
||||
return len;
|
||||
|
||||
err_try_restore:
|
||||
input_free_device(new_dev);
|
||||
priv->mode = old_mode;
|
||||
hgpk_reset_device(psmouse, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL,
|
||||
attr_show_mode, attr_set_mode);
|
||||
|
||||
static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,
|
||||
void *data, char *buf)
|
||||
{
|
||||
@ -401,6 +904,8 @@ static void hgpk_disconnect(struct psmouse *psmouse)
|
||||
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_powered.dattr);
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_hgpk_mode.dattr);
|
||||
|
||||
if (psmouse->model >= HGPK_MODEL_C)
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
@ -416,14 +921,13 @@ static void hgpk_recalib_work(struct work_struct *work)
|
||||
struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);
|
||||
struct psmouse *psmouse = priv->psmouse;
|
||||
|
||||
hgpk_dbg(psmouse, "recalibrating touchpad..\n");
|
||||
|
||||
if (hgpk_force_recalibrate(psmouse))
|
||||
hgpk_err(psmouse, "recalibration failed!\n");
|
||||
}
|
||||
|
||||
static int hgpk_register(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
int err;
|
||||
|
||||
/* register handlers */
|
||||
@ -431,13 +935,14 @@ static int hgpk_register(struct psmouse *psmouse)
|
||||
psmouse->poll = hgpk_poll;
|
||||
psmouse->disconnect = hgpk_disconnect;
|
||||
psmouse->reconnect = hgpk_reconnect;
|
||||
psmouse->pktsize = 3;
|
||||
|
||||
/* Disable the idle resync. */
|
||||
psmouse->resync_time = 0;
|
||||
/* Reset after a lot of bad bytes. */
|
||||
psmouse->resetafter = 1024;
|
||||
|
||||
hgpk_setup_input_device(psmouse->dev, NULL, priv->mode);
|
||||
|
||||
err = device_create_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_powered.dattr);
|
||||
if (err) {
|
||||
@ -445,6 +950,13 @@ static int hgpk_register(struct psmouse *psmouse)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = device_create_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_hgpk_mode.dattr);
|
||||
if (err) {
|
||||
hgpk_err(psmouse, "Failed creating 'hgpk_mode' sysfs node\n");
|
||||
goto err_remove_powered;
|
||||
}
|
||||
|
||||
/* C-series touchpads added the recalibrate command */
|
||||
if (psmouse->model >= HGPK_MODEL_C) {
|
||||
err = device_create_file(&psmouse->ps2dev.serio->dev,
|
||||
@ -452,30 +964,40 @@ static int hgpk_register(struct psmouse *psmouse)
|
||||
if (err) {
|
||||
hgpk_err(psmouse,
|
||||
"Failed creating 'recalibrate' sysfs node\n");
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_powered.dattr);
|
||||
return err;
|
||||
goto err_remove_mode;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_mode:
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_hgpk_mode.dattr);
|
||||
err_remove_powered:
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_powered.dattr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hgpk_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv;
|
||||
int err = -ENOMEM;
|
||||
int err;
|
||||
|
||||
priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
if (!priv) {
|
||||
err = -ENOMEM;
|
||||
goto alloc_fail;
|
||||
}
|
||||
|
||||
psmouse->private = priv;
|
||||
|
||||
priv->psmouse = psmouse;
|
||||
priv->powered = true;
|
||||
priv->mode = hgpk_default_mode;
|
||||
INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
|
||||
|
||||
err = psmouse_reset(psmouse);
|
||||
err = hgpk_reset_device(psmouse, false);
|
||||
if (err)
|
||||
goto init_fail;
|
||||
|
||||
@ -531,3 +1053,14 @@ int hgpk_detect(struct psmouse *psmouse, bool set_properties)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hgpk_module_init(void)
|
||||
{
|
||||
hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name,
|
||||
strlen(hgpk_mode_name));
|
||||
if (hgpk_default_mode == HGPK_MODE_INVALID) {
|
||||
hgpk_default_mode = HGPK_MODE_MOUSE;
|
||||
strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE],
|
||||
sizeof(hgpk_mode_name));
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
#ifndef _HGPK_H
|
||||
#define _HGPK_H
|
||||
|
||||
#define HGPK_GS 0xff /* The GlideSensor */
|
||||
#define HGPK_PT 0xcf /* The PenTablet */
|
||||
|
||||
enum hgpk_model_t {
|
||||
HGPK_MODEL_PREA = 0x0a, /* pre-B1s */
|
||||
HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */
|
||||
@ -13,12 +16,34 @@ enum hgpk_model_t {
|
||||
HGPK_MODEL_D = 0x50, /* C1, mass production */
|
||||
};
|
||||
|
||||
enum hgpk_spew_flag {
|
||||
NO_SPEW,
|
||||
MAYBE_SPEWING,
|
||||
SPEW_DETECTED,
|
||||
RECALIBRATING,
|
||||
};
|
||||
|
||||
#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */
|
||||
|
||||
enum hgpk_mode {
|
||||
HGPK_MODE_MOUSE,
|
||||
HGPK_MODE_GLIDESENSOR,
|
||||
HGPK_MODE_PENTABLET,
|
||||
HGPK_MODE_INVALID
|
||||
};
|
||||
|
||||
struct hgpk_data {
|
||||
struct psmouse *psmouse;
|
||||
enum hgpk_mode mode;
|
||||
bool powered;
|
||||
int count, x_tally, y_tally; /* hardware workaround stuff */
|
||||
enum hgpk_spew_flag spew_flag;
|
||||
int spew_count, x_tally, y_tally; /* spew detection */
|
||||
unsigned long recalib_window;
|
||||
struct delayed_work recalib_wq;
|
||||
int abs_x, abs_y;
|
||||
int dupe_count;
|
||||
int xbigj, ybigj, xlast, ylast; /* jumpiness detection */
|
||||
int xsaw_secondary, ysaw_secondary; /* jumpiness detection */
|
||||
};
|
||||
|
||||
#define hgpk_dbg(psmouse, format, arg...) \
|
||||
@ -33,9 +58,13 @@ struct hgpk_data {
|
||||
dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg)
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_OLPC
|
||||
void hgpk_module_init(void);
|
||||
int hgpk_detect(struct psmouse *psmouse, bool set_properties);
|
||||
int hgpk_init(struct psmouse *psmouse);
|
||||
#else
|
||||
static inline void hgpk_module_init(void)
|
||||
{
|
||||
}
|
||||
static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties)
|
||||
{
|
||||
return -ENODEV;
|
||||
|
@ -1711,6 +1711,7 @@ static int __init psmouse_init(void)
|
||||
|
||||
lifebook_module_init();
|
||||
synaptics_module_init();
|
||||
hgpk_module_init();
|
||||
|
||||
kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
|
||||
if (!kpsmoused_wq) {
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/libps2.h>
|
||||
#include <linux/slab.h>
|
||||
@ -279,6 +279,25 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
|
||||
synaptics_mode_cmd(psmouse, priv->mode);
|
||||
}
|
||||
|
||||
static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
|
||||
{
|
||||
static unsigned char param = 0xc8;
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
|
||||
if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
|
||||
return 0;
|
||||
|
||||
if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
|
||||
return -1;
|
||||
if (ps2_command(&psmouse->ps2dev, ¶m, PSMOUSE_CMD_SETRATE))
|
||||
return -1;
|
||||
|
||||
/* Advanced gesture mode also sends multi finger data */
|
||||
priv->capabilities |= BIT(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Synaptics pass-through PS/2 port support
|
||||
****************************************************************************/
|
||||
@ -380,7 +399,9 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
||||
* Functions to interpret the absolute mode packets
|
||||
****************************************************************************/
|
||||
|
||||
static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
|
||||
static int synaptics_parse_hw_state(const unsigned char buf[],
|
||||
struct synaptics_data *priv,
|
||||
struct synaptics_hw_state *hw)
|
||||
{
|
||||
memset(hw, 0, sizeof(struct synaptics_hw_state));
|
||||
|
||||
@ -397,6 +418,14 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
|
||||
((buf[0] & 0x04) >> 1) |
|
||||
((buf[3] & 0x04) >> 2));
|
||||
|
||||
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
|
||||
/* Gesture packet: (x, y, z) at half resolution */
|
||||
priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
|
||||
priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
|
||||
priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
||||
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
||||
|
||||
@ -452,6 +481,36 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
|
||||
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
||||
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
|
||||
{
|
||||
input_mt_slot(dev, slot);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
|
||||
if (active) {
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y,
|
||||
YMAX_NOMINAL + YMIN_NOMINAL - y);
|
||||
}
|
||||
}
|
||||
|
||||
static void synaptics_report_semi_mt_data(struct input_dev *dev,
|
||||
const struct synaptics_hw_state *a,
|
||||
const struct synaptics_hw_state *b,
|
||||
int num_fingers)
|
||||
{
|
||||
if (num_fingers >= 2) {
|
||||
set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y));
|
||||
set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y));
|
||||
} else if (num_fingers == 1) {
|
||||
set_slot(dev, 0, true, a->x, a->y);
|
||||
set_slot(dev, 1, false, 0, 0);
|
||||
} else {
|
||||
set_slot(dev, 0, false, 0, 0);
|
||||
set_slot(dev, 1, false, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -466,7 +525,8 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
int finger_width;
|
||||
int i;
|
||||
|
||||
synaptics_parse_hw_state(psmouse->packet, priv, &hw);
|
||||
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
|
||||
return;
|
||||
|
||||
if (hw.scroll) {
|
||||
priv->scroll += hw.scroll;
|
||||
@ -488,7 +548,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hw.z > 0) {
|
||||
if (hw.z > 0 && hw.x > 1) {
|
||||
num_fingers = 1;
|
||||
finger_width = 5;
|
||||
if (SYN_CAP_EXTENDED(priv->capabilities)) {
|
||||
@ -512,6 +572,9 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
finger_width = 0;
|
||||
}
|
||||
|
||||
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
|
||||
synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
|
||||
|
||||
/* Post events
|
||||
* BTN_TOUCH has to be first as mousedev relies on it when doing
|
||||
* absolute -> relative conversion
|
||||
@ -519,7 +582,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
|
||||
if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
|
||||
|
||||
if (hw.z > 0) {
|
||||
if (num_fingers > 0) {
|
||||
input_report_abs(dev, ABS_X, hw.x);
|
||||
input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
|
||||
}
|
||||
@ -622,6 +685,8 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, dev->propbit);
|
||||
|
||||
__set_bit(EV_ABS, dev->evbit);
|
||||
input_set_abs_params(dev, ABS_X,
|
||||
XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
|
||||
@ -629,6 +694,15 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
||||
YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
|
||||
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
|
||||
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
|
||||
priv->x_max ?: XMAX_NOMINAL, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
|
||||
priv->y_max ?: YMAX_NOMINAL, 0, 0);
|
||||
}
|
||||
|
||||
if (SYN_CAP_PALMDETECT(priv->capabilities))
|
||||
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
|
||||
|
||||
@ -663,6 +737,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
||||
input_abs_set_res(dev, ABS_Y, priv->y_res);
|
||||
|
||||
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
|
||||
/* Clickpads report only left button */
|
||||
__clear_bit(BTN_RIGHT, dev->keybit);
|
||||
__clear_bit(BTN_MIDDLE, dev->keybit);
|
||||
@ -702,6 +777,11 @@ static int synaptics_reconnect(struct psmouse *psmouse)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (synaptics_set_advanced_gesture_mode(psmouse)) {
|
||||
printk(KERN_ERR "Advanced gesture mode reconnect failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -744,15 +824,45 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool broken_olpc_ec;
|
||||
|
||||
static const struct dmi_system_id __initconst olpc_dmi_table[] = {
|
||||
#if defined(CONFIG_DMI) && defined(CONFIG_OLPC)
|
||||
{
|
||||
/* OLPC XO-1 or XO-1.5 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
#endif
|
||||
};
|
||||
|
||||
void __init synaptics_module_init(void)
|
||||
{
|
||||
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
|
||||
broken_olpc_ec = dmi_check_system(olpc_dmi_table);
|
||||
}
|
||||
|
||||
int synaptics_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv;
|
||||
|
||||
/*
|
||||
* The OLPC XO has issues with Synaptics' absolute mode; similarly to
|
||||
* the HGPK, it quickly degrades and the hardware becomes jumpy and
|
||||
* overly sensitive. Not only that, but the constant packet spew
|
||||
* (even at a lowered 40pps rate) overloads the EC such that key
|
||||
* presses on the keyboard are missed. Given all of that, don't
|
||||
* even attempt to use Synaptics mode. Relative mode seems to work
|
||||
* just fine.
|
||||
*/
|
||||
if (broken_olpc_ec) {
|
||||
printk(KERN_INFO "synaptics: OLPC XO detected, not enabling Synaptics protocol.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
@ -769,6 +879,11 @@ int synaptics_init(struct psmouse *psmouse)
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (synaptics_set_advanced_gesture_mode(psmouse)) {
|
||||
printk(KERN_ERR "Advanced gesture mode init failed.\n");
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
|
||||
|
||||
printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n",
|
||||
@ -802,8 +917,8 @@ int synaptics_init(struct psmouse *psmouse)
|
||||
|
||||
/*
|
||||
* Toshiba's KBC seems to have trouble handling data from
|
||||
* Synaptics as full rate, switch to lower rate which is roughly
|
||||
* thye same as rate of standard PS/2 mouse.
|
||||
* Synaptics at full rate. Switch to a lower rate (roughly
|
||||
* the same rate as a standard PS/2 mouse).
|
||||
*/
|
||||
if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
|
||||
printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
|
||||
|
@ -54,6 +54,7 @@
|
||||
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
|
||||
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
|
||||
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
|
||||
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
|
||||
|
||||
/* synaptics modes query bits */
|
||||
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
|
||||
@ -113,6 +114,8 @@ struct synaptics_data {
|
||||
int scroll;
|
||||
|
||||
struct serio *pt_port; /* Pass-through serio port */
|
||||
|
||||
struct synaptics_hw_state mt; /* current gesture packet */
|
||||
};
|
||||
|
||||
void synaptics_module_init(void);
|
||||
|
@ -9,6 +9,8 @@
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define MOUSEDEV_MINOR_BASE 32
|
||||
#define MOUSEDEV_MINORS 32
|
||||
#define MOUSEDEV_MIX 31
|
||||
@ -977,7 +979,7 @@ static int mousedev_connect(struct input_handler *handler,
|
||||
break;
|
||||
|
||||
if (minor == MOUSEDEV_MINORS) {
|
||||
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
|
||||
pr_err("no more free mousedev devices\n");
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
@ -1087,13 +1089,13 @@ static int __init mousedev_init(void)
|
||||
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
|
||||
error = misc_register(&psaux_mouse);
|
||||
if (error)
|
||||
printk(KERN_WARNING "mice: could not register psaux device, "
|
||||
"error: %d\n", error);
|
||||
pr_warning("could not register psaux device, error: %d\n",
|
||||
error);
|
||||
else
|
||||
psaux_registered = 1;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
|
||||
pr_info("PS/2 mouse device common for all mice\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -172,6 +172,5 @@ static void __exit ams_delta_serio_exit(void)
|
||||
free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
|
||||
gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
|
||||
gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
|
||||
kfree(ams_delta_serio);
|
||||
}
|
||||
module_exit(ams_delta_serio_exit);
|
||||
|
@ -191,6 +191,9 @@ static int __devinit ct82c710_probe(struct platform_device *dev)
|
||||
|
||||
serio_register_port(ct82c710_port);
|
||||
|
||||
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
|
||||
(unsigned long long)CT82C710_DATA, CT82C710_IRQ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -237,11 +240,6 @@ static int __init ct82c710_init(void)
|
||||
if (error)
|
||||
goto err_free_device;
|
||||
|
||||
serio_register_port(ct82c710_port);
|
||||
|
||||
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
|
||||
(unsigned long long)CT82C710_DATA, CT82C710_IRQ);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_device:
|
||||
|
@ -932,6 +932,11 @@ int hil_mlc_register(hil_mlc *mlc)
|
||||
hil_mlc_copy_di_scratch(mlc, i);
|
||||
mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL);
|
||||
mlc->serio[i] = mlc_serio;
|
||||
if (!mlc->serio[i]) {
|
||||
for (; i >= 0; i--)
|
||||
kfree(mlc->serio[i]);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
|
||||
snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
|
||||
mlc_serio->id = hil_mlc_serio_id;
|
||||
|
@ -305,6 +305,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
|
||||
static int __init hp_sdc_mlc_init(void)
|
||||
{
|
||||
hil_mlc *mlc = &hp_sdc_mlc;
|
||||
int err;
|
||||
|
||||
#ifdef __mc68000__
|
||||
if (!MACH_IS_HP300)
|
||||
@ -323,22 +324,21 @@ static int __init hp_sdc_mlc_init(void)
|
||||
mlc->out = &hp_sdc_mlc_out;
|
||||
mlc->priv = &hp_sdc_mlc_priv;
|
||||
|
||||
if (hil_mlc_register(mlc)) {
|
||||
err = hil_mlc_register(mlc);
|
||||
if (err) {
|
||||
printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
|
||||
goto err0;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
|
||||
printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
|
||||
goto err1;
|
||||
if (hil_mlc_unregister(mlc))
|
||||
printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
|
||||
"This is bad. Could cause an oops.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err1:
|
||||
if (hil_mlc_unregister(mlc))
|
||||
printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
|
||||
"This is bad. Could cause an oops.\n");
|
||||
err0:
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void __exit hp_sdc_mlc_exit(void)
|
||||
|
@ -552,6 +552,13 @@ static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
|
||||
* have turned up in 2007 that also need this again.
|
||||
*/
|
||||
static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
|
||||
{
|
||||
/* Acer Aspire 5100 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Acer Aspire 5610 */
|
||||
.matches = {
|
||||
@ -752,7 +759,7 @@ static int __init i8042_pnp_init(void)
|
||||
#endif
|
||||
|
||||
if (i8042_nopnp) {
|
||||
printk(KERN_INFO "i8042: PNP detection disabled\n");
|
||||
pr_info("PNP detection disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -769,7 +776,7 @@ static int __init i8042_pnp_init(void)
|
||||
#if defined(__ia64__)
|
||||
return -ENODEV;
|
||||
#else
|
||||
printk(KERN_INFO "PNP: No PS/2 controller found. Probing ports directly.\n");
|
||||
pr_info("PNP: No PS/2 controller found. Probing ports directly.\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@ -781,7 +788,7 @@ static int __init i8042_pnp_init(void)
|
||||
snprintf(aux_irq_str, sizeof(aux_irq_str),
|
||||
"%d", i8042_pnp_aux_irq);
|
||||
|
||||
printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
|
||||
pr_info("PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
|
||||
i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
|
||||
i8042_pnp_aux_name,
|
||||
i8042_pnp_data_reg, i8042_pnp_command_reg,
|
||||
@ -798,9 +805,7 @@ static int __init i8042_pnp_init(void)
|
||||
if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) &&
|
||||
i8042_pnp_data_reg != i8042_data_reg) ||
|
||||
!i8042_pnp_data_reg) {
|
||||
printk(KERN_WARNING
|
||||
"PNP: PS/2 controller has invalid data port %#x; "
|
||||
"using default %#x\n",
|
||||
pr_warn("PNP: PS/2 controller has invalid data port %#x; using default %#x\n",
|
||||
i8042_pnp_data_reg, i8042_data_reg);
|
||||
i8042_pnp_data_reg = i8042_data_reg;
|
||||
pnp_data_busted = true;
|
||||
@ -809,33 +814,27 @@ static int __init i8042_pnp_init(void)
|
||||
if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) &&
|
||||
i8042_pnp_command_reg != i8042_command_reg) ||
|
||||
!i8042_pnp_command_reg) {
|
||||
printk(KERN_WARNING
|
||||
"PNP: PS/2 controller has invalid command port %#x; "
|
||||
"using default %#x\n",
|
||||
pr_warn("PNP: PS/2 controller has invalid command port %#x; using default %#x\n",
|
||||
i8042_pnp_command_reg, i8042_command_reg);
|
||||
i8042_pnp_command_reg = i8042_command_reg;
|
||||
pnp_data_busted = true;
|
||||
}
|
||||
|
||||
if (!i8042_nokbd && !i8042_pnp_kbd_irq) {
|
||||
printk(KERN_WARNING
|
||||
"PNP: PS/2 controller doesn't have KBD irq; "
|
||||
"using default %d\n", i8042_kbd_irq);
|
||||
pr_warn("PNP: PS/2 controller doesn't have KBD irq; using default %d\n",
|
||||
i8042_kbd_irq);
|
||||
i8042_pnp_kbd_irq = i8042_kbd_irq;
|
||||
pnp_data_busted = true;
|
||||
}
|
||||
|
||||
if (!i8042_noaux && !i8042_pnp_aux_irq) {
|
||||
if (!pnp_data_busted && i8042_pnp_kbd_irq) {
|
||||
printk(KERN_WARNING
|
||||
"PNP: PS/2 appears to have AUX port disabled, "
|
||||
"if this is incorrect please boot with "
|
||||
"i8042.nopnp\n");
|
||||
pr_warn("PNP: PS/2 appears to have AUX port disabled, "
|
||||
"if this is incorrect please boot with i8042.nopnp\n");
|
||||
i8042_noaux = true;
|
||||
} else {
|
||||
printk(KERN_WARNING
|
||||
"PNP: PS/2 controller doesn't have AUX irq; "
|
||||
"using default %d\n", i8042_aux_irq);
|
||||
pr_warn("PNP: PS/2 controller doesn't have AUX irq; using default %d\n",
|
||||
i8042_aux_irq);
|
||||
i8042_pnp_aux_irq = i8042_aux_irq;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
@ -225,8 +227,8 @@ static int i8042_flush(void)
|
||||
udelay(50);
|
||||
data = i8042_read_data();
|
||||
i++;
|
||||
dbg("%02x <- i8042 (flush, %s)", data,
|
||||
str & I8042_STR_AUXDATA ? "aux" : "kbd");
|
||||
dbg("%02x <- i8042 (flush, %s)\n",
|
||||
data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||
@ -253,32 +255,32 @@ static int __i8042_command(unsigned char *param, int command)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dbg("%02x -> i8042 (command)", command & 0xff);
|
||||
dbg("%02x -> i8042 (command)\n", command & 0xff);
|
||||
i8042_write_command(command & 0xff);
|
||||
|
||||
for (i = 0; i < ((command >> 12) & 0xf); i++) {
|
||||
error = i8042_wait_write();
|
||||
if (error)
|
||||
return error;
|
||||
dbg("%02x -> i8042 (parameter)", param[i]);
|
||||
dbg("%02x -> i8042 (parameter)\n", param[i]);
|
||||
i8042_write_data(param[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ((command >> 8) & 0xf); i++) {
|
||||
error = i8042_wait_read();
|
||||
if (error) {
|
||||
dbg(" -- i8042 (timeout)");
|
||||
dbg(" -- i8042 (timeout)\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (command == I8042_CMD_AUX_LOOP &&
|
||||
!(i8042_read_status() & I8042_STR_AUXDATA)) {
|
||||
dbg(" -- i8042 (auxerr)");
|
||||
dbg(" -- i8042 (auxerr)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
param[i] = i8042_read_data();
|
||||
dbg("%02x <- i8042 (return)", param[i]);
|
||||
dbg("%02x <- i8042 (return)\n", param[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -309,7 +311,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
|
||||
spin_lock_irqsave(&i8042_lock, flags);
|
||||
|
||||
if (!(retval = i8042_wait_write())) {
|
||||
dbg("%02x -> i8042 (kbd-data)", c);
|
||||
dbg("%02x -> i8042 (kbd-data)\n", c);
|
||||
i8042_write_data(c);
|
||||
}
|
||||
|
||||
@ -355,17 +357,14 @@ static void i8042_port_close(struct serio *serio)
|
||||
|
||||
i8042_ctr &= ~irq_bit;
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
|
||||
printk(KERN_WARNING
|
||||
"i8042.c: Can't write CTR while closing %s port.\n",
|
||||
port_name);
|
||||
pr_warn("Can't write CTR while closing %s port\n", port_name);
|
||||
|
||||
udelay(50);
|
||||
|
||||
i8042_ctr &= ~disable_bit;
|
||||
i8042_ctr |= irq_bit;
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
|
||||
printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n",
|
||||
port_name);
|
||||
pr_err("Can't reactivate %s port\n", port_name);
|
||||
|
||||
/*
|
||||
* See if there is any data appeared while we were messing with
|
||||
@ -456,7 +455,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
||||
str = i8042_read_status();
|
||||
if (unlikely(~str & I8042_STR_OBF)) {
|
||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||
if (irq) dbg("Interrupt %d, without any data", irq);
|
||||
if (irq)
|
||||
dbg("Interrupt %d, without any data\n", irq);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
@ -469,7 +469,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
||||
|
||||
dfl = 0;
|
||||
if (str & I8042_STR_MUXERR) {
|
||||
dbg("MUX error, status is %02x, data is %02x", str, data);
|
||||
dbg("MUX error, status is %02x, data is %02x\n",
|
||||
str, data);
|
||||
/*
|
||||
* When MUXERR condition is signalled the data register can only contain
|
||||
* 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
|
||||
@ -512,7 +513,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
||||
port = &i8042_ports[port_no];
|
||||
serio = port->exists ? port->serio : NULL;
|
||||
|
||||
dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
|
||||
dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n",
|
||||
data, port_no, irq,
|
||||
dfl & SERIO_PARITY ? ", bad parity" : "",
|
||||
dfl & SERIO_TIMEOUT ? ", timeout" : "");
|
||||
@ -540,7 +541,7 @@ static int i8042_enable_kbd_port(void)
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||
i8042_ctr &= ~I8042_CTR_KBDINT;
|
||||
i8042_ctr |= I8042_CTR_KBDDIS;
|
||||
printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
|
||||
pr_err("Failed to enable KBD port\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -559,7 +560,7 @@ static int i8042_enable_aux_port(void)
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||
i8042_ctr &= ~I8042_CTR_AUXINT;
|
||||
i8042_ctr |= I8042_CTR_AUXDIS;
|
||||
printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
|
||||
pr_err("Failed to enable AUX port\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -641,7 +642,7 @@ static int __init i8042_check_mux(void)
|
||||
if (i8042_set_mux_mode(true, &mux_version))
|
||||
return -1;
|
||||
|
||||
printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
|
||||
pr_info("Detected active multiplexing controller, rev %d.%d\n",
|
||||
(mux_version >> 4) & 0xf, mux_version & 0xf);
|
||||
|
||||
/*
|
||||
@ -651,7 +652,7 @@ static int __init i8042_check_mux(void)
|
||||
i8042_ctr &= ~I8042_CTR_AUXINT;
|
||||
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||
printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
|
||||
pr_err("Failed to disable AUX port, can't use MUX\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -676,8 +677,8 @@ static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id)
|
||||
str = i8042_read_status();
|
||||
if (str & I8042_STR_OBF) {
|
||||
data = i8042_read_data();
|
||||
dbg("%02x <- i8042 (aux_test_irq, %s)",
|
||||
data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
|
||||
dbg("%02x <- i8042 (aux_test_irq, %s)\n",
|
||||
data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
|
||||
if (i8042_irq_being_tested &&
|
||||
data == 0xa5 && (str & I8042_STR_AUXDATA))
|
||||
complete(&i8042_aux_irq_delivered);
|
||||
@ -770,8 +771,8 @@ static int __init i8042_check_aux(void)
|
||||
*/
|
||||
|
||||
if (i8042_toggle_aux(false)) {
|
||||
printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
|
||||
printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n");
|
||||
pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
|
||||
pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n");
|
||||
}
|
||||
|
||||
if (i8042_toggle_aux(true))
|
||||
@ -819,7 +820,7 @@ static int __init i8042_check_aux(void)
|
||||
* AUX IRQ was never delivered so we need to flush the controller to
|
||||
* get rid of the byte we put there; otherwise keyboard may not work.
|
||||
*/
|
||||
dbg(" -- i8042 (aux irq test timeout)");
|
||||
dbg(" -- i8042 (aux irq test timeout)\n");
|
||||
i8042_flush();
|
||||
retval = -1;
|
||||
}
|
||||
@ -845,7 +846,7 @@ static int __init i8042_check_aux(void)
|
||||
static int i8042_controller_check(void)
|
||||
{
|
||||
if (i8042_flush() == I8042_BUFFER_SIZE) {
|
||||
printk(KERN_ERR "i8042.c: No controller found.\n");
|
||||
pr_err("No controller found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -864,15 +865,15 @@ static int i8042_controller_selftest(void)
|
||||
do {
|
||||
|
||||
if (i8042_command(¶m, I8042_CMD_CTL_TEST)) {
|
||||
printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
|
||||
pr_err("i8042 controller self test timeout\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (param == I8042_RET_CTL_TEST)
|
||||
return 0;
|
||||
|
||||
printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
|
||||
param, I8042_RET_CTL_TEST);
|
||||
pr_err("i8042 controller selftest failed. (%#x != %#x)\n",
|
||||
param, I8042_RET_CTL_TEST);
|
||||
msleep(50);
|
||||
} while (i++ < 5);
|
||||
|
||||
@ -883,8 +884,7 @@ static int i8042_controller_selftest(void)
|
||||
* and user will still get a working keyboard. This is especially
|
||||
* important on netbooks. On other arches we trust hardware more.
|
||||
*/
|
||||
printk(KERN_INFO
|
||||
"i8042: giving up on controller selftest, continuing anyway...\n");
|
||||
pr_info("giving up on controller selftest, continuing anyway...\n");
|
||||
return 0;
|
||||
#else
|
||||
return -EIO;
|
||||
@ -909,8 +909,7 @@ static int i8042_controller_init(void)
|
||||
|
||||
do {
|
||||
if (n >= 10) {
|
||||
printk(KERN_ERR
|
||||
"i8042.c: Unable to get stable CTR read.\n");
|
||||
pr_err("Unable to get stable CTR read\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -918,8 +917,7 @@ static int i8042_controller_init(void)
|
||||
udelay(50);
|
||||
|
||||
if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) {
|
||||
printk(KERN_ERR
|
||||
"i8042.c: Can't read CTR while initializing i8042.\n");
|
||||
pr_err("Can't read CTR while initializing i8042\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -943,7 +941,7 @@ static int i8042_controller_init(void)
|
||||
if (i8042_unlock)
|
||||
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
|
||||
else
|
||||
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
|
||||
pr_warn("Warning: Keylock active\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&i8042_lock, flags);
|
||||
|
||||
@ -970,7 +968,7 @@ static int i8042_controller_init(void)
|
||||
*/
|
||||
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||
printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
|
||||
pr_err("Can't write CTR while initializing i8042\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -1000,7 +998,7 @@ static void i8042_controller_reset(void)
|
||||
i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
|
||||
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
|
||||
printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n");
|
||||
pr_warn("Can't write CTR while resetting\n");
|
||||
|
||||
/*
|
||||
* Disable MUX mode if present.
|
||||
@ -1021,7 +1019,7 @@ static void i8042_controller_reset(void)
|
||||
*/
|
||||
|
||||
if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
|
||||
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
|
||||
pr_warn("Can't restore CTR\n");
|
||||
}
|
||||
|
||||
|
||||
@ -1045,14 +1043,14 @@ static long i8042_panic_blink(int state)
|
||||
led = (state) ? 0x01 | 0x04 : 0;
|
||||
while (i8042_read_status() & I8042_STR_IBF)
|
||||
DELAY;
|
||||
dbg("%02x -> i8042 (panic blink)", 0xed);
|
||||
dbg("%02x -> i8042 (panic blink)\n", 0xed);
|
||||
i8042_suppress_kbd_ack = 2;
|
||||
i8042_write_data(0xed); /* set leds */
|
||||
DELAY;
|
||||
while (i8042_read_status() & I8042_STR_IBF)
|
||||
DELAY;
|
||||
DELAY;
|
||||
dbg("%02x -> i8042 (panic blink)", led);
|
||||
dbg("%02x -> i8042 (panic blink)\n", led);
|
||||
i8042_write_data(led);
|
||||
DELAY;
|
||||
return delay;
|
||||
@ -1068,9 +1066,7 @@ static void i8042_dritek_enable(void)
|
||||
|
||||
error = i8042_command(¶m, 0x1059);
|
||||
if (error)
|
||||
printk(KERN_WARNING
|
||||
"Failed to enable DRITEK extension: %d\n",
|
||||
error);
|
||||
pr_warn("Failed to enable DRITEK extension: %d\n", error);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1105,10 +1101,10 @@ static int i8042_controller_resume(bool force_reset)
|
||||
i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS;
|
||||
i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT);
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||
printk(KERN_WARNING "i8042: Can't write CTR to resume, retrying...\n");
|
||||
pr_warn("Can't write CTR to resume, retrying...\n");
|
||||
msleep(50);
|
||||
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
|
||||
printk(KERN_ERR "i8042: CTR write retry failed\n");
|
||||
pr_err("CTR write retry failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
@ -1121,9 +1117,7 @@ static int i8042_controller_resume(bool force_reset)
|
||||
|
||||
if (i8042_mux_present) {
|
||||
if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports())
|
||||
printk(KERN_WARNING
|
||||
"i8042: failed to resume active multiplexor, "
|
||||
"mouse won't work.\n");
|
||||
pr_warn("failed to resume active multiplexor, mouse won't work\n");
|
||||
} else if (i8042_ports[I8042_AUX_PORT_NO].serio)
|
||||
i8042_enable_aux_port();
|
||||
|
||||
|
@ -89,15 +89,19 @@
|
||||
#ifdef DEBUG
|
||||
static unsigned long i8042_start_time;
|
||||
#define dbg_init() do { i8042_start_time = jiffies; } while (0)
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (i8042_debug) \
|
||||
printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" , \
|
||||
## arg, (int) (jiffies - i8042_start_time)); \
|
||||
printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \
|
||||
(int) (jiffies - i8042_start_time), ##arg); \
|
||||
} while (0)
|
||||
#else
|
||||
#define dbg_init() do { } while (0)
|
||||
#define dbg(format, arg...) do {} while (0)
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
if (0) \
|
||||
printk(KERN_DEBUG pr_fmt(format), ##arg); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _I8042_H */
|
||||
|
@ -207,7 +207,7 @@ static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
|
||||
err_out:
|
||||
while (--i >= 0)
|
||||
kfree(psm->ports[i].serio);
|
||||
kfree(serio);
|
||||
kfree(psm);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,9 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
@ -44,7 +43,7 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* serio_mutex protects entire serio subsystem and is taken every time
|
||||
* serio port or driver registrered or unregistered.
|
||||
* serio port or driver registered or unregistered.
|
||||
*/
|
||||
static DEFINE_MUTEX(serio_mutex);
|
||||
|
||||
@ -165,8 +164,95 @@ struct serio_event {
|
||||
|
||||
static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */
|
||||
static LIST_HEAD(serio_event_list);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
|
||||
static struct task_struct *serio_task;
|
||||
|
||||
static struct serio_event *serio_get_event(void)
|
||||
{
|
||||
struct serio_event *event = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&serio_event_lock, flags);
|
||||
|
||||
if (!list_empty(&serio_event_list)) {
|
||||
event = list_first_entry(&serio_event_list,
|
||||
struct serio_event, node);
|
||||
list_del_init(&event->node);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&serio_event_lock, flags);
|
||||
return event;
|
||||
}
|
||||
|
||||
static void serio_free_event(struct serio_event *event)
|
||||
{
|
||||
module_put(event->owner);
|
||||
kfree(event);
|
||||
}
|
||||
|
||||
static void serio_remove_duplicate_events(struct serio_event *event)
|
||||
{
|
||||
struct serio_event *e, *next;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&serio_event_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(e, next, &serio_event_list, node) {
|
||||
if (event->object == e->object) {
|
||||
/*
|
||||
* If this event is of different type we should not
|
||||
* look further - we only suppress duplicate events
|
||||
* that were sent back-to-back.
|
||||
*/
|
||||
if (event->type != e->type)
|
||||
break;
|
||||
|
||||
list_del_init(&e->node);
|
||||
serio_free_event(e);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&serio_event_lock, flags);
|
||||
}
|
||||
|
||||
static void serio_handle_event(struct work_struct *work)
|
||||
{
|
||||
struct serio_event *event;
|
||||
|
||||
mutex_lock(&serio_mutex);
|
||||
|
||||
while ((event = serio_get_event())) {
|
||||
|
||||
switch (event->type) {
|
||||
|
||||
case SERIO_REGISTER_PORT:
|
||||
serio_add_port(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RECONNECT_PORT:
|
||||
serio_reconnect_port(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RESCAN_PORT:
|
||||
serio_disconnect_port(event->object);
|
||||
serio_find_driver(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RECONNECT_SUBTREE:
|
||||
serio_reconnect_subtree(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_ATTACH_DRIVER:
|
||||
serio_attach_driver(event->object);
|
||||
break;
|
||||
}
|
||||
|
||||
serio_remove_duplicate_events(event);
|
||||
serio_free_event(event);
|
||||
}
|
||||
|
||||
mutex_unlock(&serio_mutex);
|
||||
}
|
||||
|
||||
static DECLARE_WORK(serio_event_work, serio_handle_event);
|
||||
|
||||
static int serio_queue_event(void *object, struct module *owner,
|
||||
enum serio_event_type event_type)
|
||||
@ -212,101 +298,13 @@ static int serio_queue_event(void *object, struct module *owner,
|
||||
event->owner = owner;
|
||||
|
||||
list_add_tail(&event->node, &serio_event_list);
|
||||
wake_up(&serio_wait);
|
||||
schedule_work(&serio_event_work);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&serio_event_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void serio_free_event(struct serio_event *event)
|
||||
{
|
||||
module_put(event->owner);
|
||||
kfree(event);
|
||||
}
|
||||
|
||||
static void serio_remove_duplicate_events(struct serio_event *event)
|
||||
{
|
||||
struct serio_event *e, *next;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&serio_event_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(e, next, &serio_event_list, node) {
|
||||
if (event->object == e->object) {
|
||||
/*
|
||||
* If this event is of different type we should not
|
||||
* look further - we only suppress duplicate events
|
||||
* that were sent back-to-back.
|
||||
*/
|
||||
if (event->type != e->type)
|
||||
break;
|
||||
|
||||
list_del_init(&e->node);
|
||||
serio_free_event(e);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&serio_event_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
static struct serio_event *serio_get_event(void)
|
||||
{
|
||||
struct serio_event *event = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&serio_event_lock, flags);
|
||||
|
||||
if (!list_empty(&serio_event_list)) {
|
||||
event = list_first_entry(&serio_event_list,
|
||||
struct serio_event, node);
|
||||
list_del_init(&event->node);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&serio_event_lock, flags);
|
||||
return event;
|
||||
}
|
||||
|
||||
static void serio_handle_event(void)
|
||||
{
|
||||
struct serio_event *event;
|
||||
|
||||
mutex_lock(&serio_mutex);
|
||||
|
||||
while ((event = serio_get_event())) {
|
||||
|
||||
switch (event->type) {
|
||||
|
||||
case SERIO_REGISTER_PORT:
|
||||
serio_add_port(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RECONNECT_PORT:
|
||||
serio_reconnect_port(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RESCAN_PORT:
|
||||
serio_disconnect_port(event->object);
|
||||
serio_find_driver(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RECONNECT_SUBTREE:
|
||||
serio_reconnect_subtree(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_ATTACH_DRIVER:
|
||||
serio_attach_driver(event->object);
|
||||
break;
|
||||
}
|
||||
|
||||
serio_remove_duplicate_events(event);
|
||||
serio_free_event(event);
|
||||
}
|
||||
|
||||
mutex_unlock(&serio_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all events that have been submitted for a given
|
||||
* object, be it serio port or driver.
|
||||
@ -356,18 +354,6 @@ static struct serio *serio_get_pending_child(struct serio *parent)
|
||||
return child;
|
||||
}
|
||||
|
||||
static int serio_thread(void *nothing)
|
||||
{
|
||||
do {
|
||||
serio_handle_event();
|
||||
wait_event_interruptible(serio_wait,
|
||||
kthread_should_stop() || !list_empty(&serio_event_list));
|
||||
} while (!kthread_should_stop());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Serio port operations
|
||||
*/
|
||||
@ -1040,21 +1026,18 @@ static int __init serio_init(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
serio_task = kthread_run(serio_thread, NULL, "kseriod");
|
||||
if (IS_ERR(serio_task)) {
|
||||
bus_unregister(&serio_bus);
|
||||
error = PTR_ERR(serio_task);
|
||||
pr_err("Failed to start kseriod, error: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit serio_exit(void)
|
||||
{
|
||||
bus_unregister(&serio_bus);
|
||||
kthread_stop(serio_task);
|
||||
|
||||
/*
|
||||
* There should not be any outstanding events but work may
|
||||
* still be scheduled so simply cancel it.
|
||||
*/
|
||||
cancel_work_sync(&serio_event_work);
|
||||
}
|
||||
|
||||
subsys_initcall(serio_init);
|
||||
|
@ -103,6 +103,7 @@ MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
#define USB_VENDOR_ID_LENOVO 0x17ef
|
||||
|
||||
struct wacom {
|
||||
dma_addr_t data_dma;
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "wacom_wac.h"
|
||||
#include "wacom.h"
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
static int wacom_penpartner_irq(struct wacom_wac *wacom)
|
||||
{
|
||||
@ -862,19 +863,21 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
|
||||
struct wacom_features *features = &wacom->features;
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *data = wacom->data;
|
||||
int sp = 0, sx = 0, sy = 0, count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int p = data[9 * i + 2];
|
||||
bool touch = p && !wacom->shared->stylus_in_proximity;
|
||||
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||
/*
|
||||
* Touch events need to be disabled while stylus is
|
||||
* in proximity because user's hand is resting on touchpad
|
||||
* and sending unwanted events. User expects tablet buttons
|
||||
* to continue working though.
|
||||
*/
|
||||
if (p && !wacom->shared->stylus_in_proximity) {
|
||||
if (touch) {
|
||||
int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
|
||||
int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
|
||||
if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
|
||||
@ -884,23 +887,10 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
|
||||
input_report_abs(input, ABS_MT_PRESSURE, p);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
if (wacom->id[i] < 0)
|
||||
wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID;
|
||||
if (!count++)
|
||||
sp = p, sx = x, sy = y;
|
||||
} else {
|
||||
wacom->id[i] = -1;
|
||||
}
|
||||
input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]);
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_TOUCH, count > 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, count == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2);
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, sp);
|
||||
input_report_abs(input, ABS_X, sx);
|
||||
input_report_abs(input, ABS_Y, sy);
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
|
||||
input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
|
||||
@ -1272,7 +1262,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
|
||||
input_mt_create_slots(input_dev, 2);
|
||||
input_mt_init_slots(input_dev, 2);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
@ -1282,8 +1272,6 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
||||
0, features->pressure_max,
|
||||
features->pressure_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
|
||||
MAX_TRACKING_ID, 0, 0);
|
||||
} else if (features->device_type == BTN_TOOL_PEN) {
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
@ -1444,11 +1432,17 @@ static struct wacom_features wacom_features_0xDA =
|
||||
{ "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xDB =
|
||||
{ "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
|
||||
static const struct wacom_features wacom_features_0x6004 =
|
||||
{ "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC };
|
||||
|
||||
#define USB_DEVICE_WACOM(prod) \
|
||||
USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \
|
||||
.driver_info = (kernel_ulong_t)&wacom_features_##prod
|
||||
|
||||
#define USB_DEVICE_LENOVO(prod) \
|
||||
USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \
|
||||
.driver_info = (kernel_ulong_t)&wacom_features_##prod
|
||||
|
||||
const struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE_WACOM(0x00) },
|
||||
{ USB_DEVICE_WACOM(0x10) },
|
||||
@ -1525,6 +1519,7 @@ const struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE_WACOM(0xE2) },
|
||||
{ USB_DEVICE_WACOM(0xE3) },
|
||||
{ USB_DEVICE_WACOM(0x47) },
|
||||
{ USB_DEVICE_LENOVO(0x6004) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, wacom_ids);
|
||||
|
@ -42,9 +42,6 @@
|
||||
#define WACOM_QUIRK_MULTI_INPUT 0x0001
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
|
||||
|
||||
/* largest reported tracking id */
|
||||
#define MAX_TRACKING_ID 0xfff
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
GRAPHIRE,
|
||||
@ -100,7 +97,6 @@ struct wacom_wac {
|
||||
int id[3];
|
||||
__u32 serial[2];
|
||||
int last_finger;
|
||||
int trk_id;
|
||||
struct wacom_features features;
|
||||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
|
@ -659,6 +659,28 @@ config TOUCHSCREEN_PCAP
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pcap_ts.
|
||||
|
||||
config TOUCHSCREEN_ST1232
|
||||
tristate "Sitronix ST1232 touchscreen controllers"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to support Sitronix ST1232
|
||||
touchscreen controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called st1232_ts.
|
||||
|
||||
config TOUCHSCREEN_STMPE
|
||||
tristate "STMicroelectronics STMPE touchscreens"
|
||||
depends on MFD_STMPE
|
||||
help
|
||||
Say Y here if you want support for STMicroelectronics
|
||||
STMPE touchscreen controllers.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stmpe-ts.
|
||||
|
||||
config TOUCHSCREEN_TPS6507X
|
||||
tristate "TPS6507x based touchscreens"
|
||||
depends on I2C
|
||||
@ -671,14 +693,4 @@ config TOUCHSCREEN_TPS6507X
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tps6507x_ts.
|
||||
|
||||
config TOUCHSCREEN_STMPE
|
||||
tristate "STMicroelectronics STMPE touchscreens"
|
||||
depends on MFD_STMPE
|
||||
help
|
||||
Say Y here if you want support for STMicroelectronics
|
||||
STMPE touchscreen controllers.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stmpe-ts.
|
||||
|
||||
endif
|
||||
|
@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||
|
@ -365,7 +365,7 @@ static int bu21013_init_chip(struct bu21013_ts_data *data)
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
|
||||
BU21013_TH_OFF_4 || BU21013_TH_OFF_3);
|
||||
BU21013_TH_OFF_4 | BU21013_TH_OFF_3);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
|
||||
return retval;
|
||||
|
@ -969,7 +969,7 @@ static int qt602240_initialize(struct qt602240_data *data)
|
||||
return error;
|
||||
|
||||
data->object_table = kcalloc(info->object_num,
|
||||
sizeof(struct qt602240_data),
|
||||
sizeof(struct qt602240_object),
|
||||
GFP_KERNEL);
|
||||
if (!data->object_table) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
@ -1324,8 +1324,9 @@ static int __devexit qt602240_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int qt602240_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct qt602240_data *data = i2c_get_clientdata(client);
|
||||
struct input_dev *input_dev = data->input_dev;
|
||||
|
||||
@ -1339,8 +1340,9 @@ static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qt602240_resume(struct i2c_client *client)
|
||||
static int qt602240_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct qt602240_data *data = i2c_get_clientdata(client);
|
||||
struct input_dev *input_dev = data->input_dev;
|
||||
|
||||
@ -1359,9 +1361,11 @@ static int qt602240_resume(struct i2c_client *client)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define qt602240_suspend NULL
|
||||
#define qt602240_resume NULL
|
||||
|
||||
static const struct dev_pm_ops qt602240_pm_ops = {
|
||||
.suspend = qt602240_suspend,
|
||||
.resume = qt602240_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id qt602240_id[] = {
|
||||
@ -1374,11 +1378,12 @@ static struct i2c_driver qt602240_driver = {
|
||||
.driver = {
|
||||
.name = "qt602240_ts",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &qt602240_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = qt602240_probe,
|
||||
.remove = __devexit_p(qt602240_remove),
|
||||
.suspend = qt602240_suspend,
|
||||
.resume = qt602240_resume,
|
||||
.id_table = qt602240_id,
|
||||
};
|
||||
|
||||
|
274
drivers/input/touchscreen/st1232.c
Normal file
274
drivers/input/touchscreen/st1232.c
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* ST1232 Touchscreen Controller Driver
|
||||
*
|
||||
* Copyright (C) 2010 Renesas Solutions Corp.
|
||||
* Tony SIM <chinyeow.sim.xt@renesas.com>
|
||||
*
|
||||
* Using code from:
|
||||
* - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ST1232_TS_NAME "st1232-ts"
|
||||
|
||||
#define MIN_X 0x00
|
||||
#define MIN_Y 0x00
|
||||
#define MAX_X 0x31f /* (800 - 1) */
|
||||
#define MAX_Y 0x1df /* (480 - 1) */
|
||||
#define MAX_AREA 0xff
|
||||
#define MAX_FINGERS 2
|
||||
|
||||
struct st1232_ts_finger {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u8 t;
|
||||
bool is_valid;
|
||||
};
|
||||
|
||||
struct st1232_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
struct st1232_ts_finger finger[MAX_FINGERS];
|
||||
};
|
||||
|
||||
static int st1232_ts_read_data(struct st1232_ts_data *ts)
|
||||
{
|
||||
struct st1232_ts_finger *finger = ts->finger;
|
||||
struct i2c_client *client = ts->client;
|
||||
struct i2c_msg msg[2];
|
||||
int error;
|
||||
u8 start_reg;
|
||||
u8 buf[10];
|
||||
|
||||
/* read touchscreen data from ST1232 */
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = &start_reg;
|
||||
start_reg = 0x10;
|
||||
|
||||
msg[1].addr = ts->client->addr;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
msg[1].len = sizeof(buf);
|
||||
msg[1].buf = buf;
|
||||
|
||||
error = i2c_transfer(client->adapter, msg, 2);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* get "valid" bits */
|
||||
finger[0].is_valid = buf[2] >> 7;
|
||||
finger[1].is_valid = buf[5] >> 7;
|
||||
|
||||
/* get xy coordinate */
|
||||
if (finger[0].is_valid) {
|
||||
finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
|
||||
finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
|
||||
finger[0].t = buf[8];
|
||||
}
|
||||
|
||||
if (finger[1].is_valid) {
|
||||
finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
|
||||
finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
|
||||
finger[1].t = buf[9];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct st1232_ts_data *ts = dev_id;
|
||||
struct st1232_ts_finger *finger = ts->finger;
|
||||
struct input_dev *input_dev = ts->input_dev;
|
||||
int count = 0;
|
||||
int i, ret;
|
||||
|
||||
ret = st1232_ts_read_data(ts);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
/* multi touch protocol */
|
||||
for (i = 0; i < MAX_FINGERS; i++) {
|
||||
if (!finger[i].is_valid)
|
||||
continue;
|
||||
|
||||
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
|
||||
input_mt_sync(input_dev);
|
||||
count++;
|
||||
}
|
||||
|
||||
/* SYN_MT_REPORT only if no contact */
|
||||
if (!count)
|
||||
input_mt_sync(input_dev);
|
||||
|
||||
/* SYN_REPORT */
|
||||
input_sync(input_dev);
|
||||
|
||||
end:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit st1232_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct st1232_ts_data *ts;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev, "no IRQ?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ts->client = client;
|
||||
ts->input_dev = input_dev;
|
||||
|
||||
input_dev->name = "st1232-touchscreen";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_SYN, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler,
|
||||
IRQF_ONESHOT, client->name, ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(ts->input_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to register %s input device\n",
|
||||
input_dev->name);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, ts);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(ts);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit st1232_ts_remove(struct i2c_client *client)
|
||||
{
|
||||
struct st1232_ts_data *ts = i2c_get_clientdata(client);
|
||||
|
||||
device_init_wakeup(&client->dev, 0);
|
||||
free_irq(client->irq, ts);
|
||||
input_unregister_device(ts->input_dev);
|
||||
kfree(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int st1232_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(client->irq);
|
||||
else
|
||||
disable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st1232_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(client->irq);
|
||||
else
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops st1232_ts_pm_ops = {
|
||||
.suspend = st1232_ts_suspend,
|
||||
.resume = st1232_ts_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id st1232_ts_id[] = {
|
||||
{ ST1232_TS_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
|
||||
|
||||
static struct i2c_driver st1232_ts_driver = {
|
||||
.probe = st1232_ts_probe,
|
||||
.remove = __devexit_p(st1232_ts_remove),
|
||||
.id_table = st1232_ts_id,
|
||||
.driver = {
|
||||
.name = ST1232_TS_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &st1232_ts_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init st1232_ts_init(void)
|
||||
{
|
||||
return i2c_add_driver(&st1232_ts_driver);
|
||||
}
|
||||
module_init(st1232_ts_init);
|
||||
|
||||
static void __exit st1232_ts_exit(void)
|
||||
{
|
||||
i2c_del_driver(&st1232_ts_driver);
|
||||
}
|
||||
module_exit(st1232_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
|
||||
MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -15,10 +15,11 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define DRIVER_DESC "Wacom W8001 serial touchscreen driver"
|
||||
|
||||
@ -37,6 +38,7 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
#define W8001_QUERY_PACKET 0x20
|
||||
|
||||
#define W8001_CMD_STOP '0'
|
||||
#define W8001_CMD_START '1'
|
||||
#define W8001_CMD_QUERY '*'
|
||||
#define W8001_CMD_TOUCHQUERY '%'
|
||||
@ -48,8 +50,6 @@ MODULE_LICENSE("GPL");
|
||||
#define W8001_PKTLEN_TPCCTL 11 /* control packet */
|
||||
#define W8001_PKTLEN_TOUCH2FG 13
|
||||
|
||||
#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */
|
||||
|
||||
struct w8001_coord {
|
||||
u8 rdy;
|
||||
u8 tsw;
|
||||
@ -87,7 +87,6 @@ struct w8001 {
|
||||
char phys[32];
|
||||
int type;
|
||||
unsigned int pktlen;
|
||||
int trkid[2];
|
||||
};
|
||||
|
||||
static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
@ -116,28 +115,23 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
|
||||
static void parse_touch(struct w8001 *w8001)
|
||||
{
|
||||
static int trkid;
|
||||
struct input_dev *dev = w8001->dev;
|
||||
unsigned char *data = w8001->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
input_mt_slot(dev, i);
|
||||
bool touch = data[0] & (1 << i);
|
||||
|
||||
if (data[0] & (1 << i)) {
|
||||
input_mt_slot(dev, i);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
|
||||
if (touch) {
|
||||
int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
|
||||
int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
|
||||
/* data[5,6] and [11,12] is finger capacity */
|
||||
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
|
||||
if (w8001->trkid[i] < 0)
|
||||
w8001->trkid[i] = trkid++ & MAX_TRACKING_ID;
|
||||
} else {
|
||||
w8001->trkid[i] = -1;
|
||||
}
|
||||
input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]);
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
@ -287,24 +281,46 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
struct w8001_coord coord;
|
||||
int error;
|
||||
|
||||
error = w8001_command(w8001, W8001_CMD_QUERY, true);
|
||||
error = w8001_command(w8001, W8001_CMD_STOP, false);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
parse_data(w8001->response, &coord);
|
||||
msleep(250); /* wait 250ms before querying the device */
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
|
||||
|
||||
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
|
||||
/* penabled? */
|
||||
error = w8001_command(w8001, W8001_CMD_QUERY, true);
|
||||
if (!error) {
|
||||
__set_bit(BTN_TOOL_PEN, dev->keybit);
|
||||
__set_bit(BTN_TOOL_RUBBER, dev->keybit);
|
||||
__set_bit(BTN_STYLUS, dev->keybit);
|
||||
__set_bit(BTN_STYLUS2, dev->keybit);
|
||||
parse_data(w8001->response, &coord);
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
|
||||
if (coord.tilt_x && coord.tilt_y) {
|
||||
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Touch enabled? */
|
||||
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
|
||||
|
||||
/*
|
||||
* Some non-touch devices may reply to the touch query. But their
|
||||
* second byte is empty, which indicates touch is not supported.
|
||||
*/
|
||||
if (!error && w8001->response[1]) {
|
||||
struct w8001_touch_query touch;
|
||||
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
|
||||
__set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
|
||||
switch (touch.sensor_id) {
|
||||
case 0:
|
||||
case 2:
|
||||
@ -318,15 +334,13 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
input_mt_create_slots(dev, 2);
|
||||
input_set_abs_params(dev, ABS_MT_TRACKING_ID,
|
||||
0, MAX_TRACKING_ID, 0, 0);
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
0, touch.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
|
||||
0, 0, 0, 0);
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -372,7 +386,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
w8001->serio = serio;
|
||||
w8001->id = serio->id.id;
|
||||
w8001->dev = input_dev;
|
||||
w8001->trkid[0] = w8001->trkid[1] = -1;
|
||||
init_completion(&w8001->cmd_done);
|
||||
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||
|
||||
@ -385,11 +398,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER);
|
||||
input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS);
|
||||
input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, w8001);
|
||||
err = serio_open(serio, drv);
|
||||
|
@ -17,6 +17,8 @@
|
||||
* Switch to grant tables together with xen-fbfront.c.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
@ -84,9 +86,8 @@ static irqreturn_t input_handler(int rq, void *dev_id)
|
||||
input_report_key(dev, event->key.keycode,
|
||||
event->key.pressed);
|
||||
else
|
||||
printk(KERN_WARNING
|
||||
"xenkbd: unhandled keycode 0x%x\n",
|
||||
event->key.keycode);
|
||||
pr_warning("unhandled keycode 0x%x\n",
|
||||
event->key.keycode);
|
||||
break;
|
||||
case XENKBD_TYPE_POS:
|
||||
input_report_abs(dev, ABS_X, event->pos.abs_x);
|
||||
@ -292,8 +293,7 @@ InitWait:
|
||||
ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
if (ret)
|
||||
printk(KERN_WARNING
|
||||
"xenkbd: can't request abs-pointer");
|
||||
pr_warning("can't request abs-pointer\n");
|
||||
}
|
||||
xenbus_switch_state(dev, XenbusStateConnected);
|
||||
break;
|
||||
|
@ -23,6 +23,8 @@ static int mouse_button3_keycode = KEY_RIGHTALT; /* right option key */
|
||||
|
||||
static struct input_dev *mac_hid_emumouse_dev;
|
||||
|
||||
static DEFINE_MUTEX(mac_hid_emumouse_mutex);
|
||||
|
||||
static int mac_hid_create_emumouse(void)
|
||||
{
|
||||
static struct lock_class_key mac_hid_emumouse_dev_event_class;
|
||||
@ -187,6 +189,10 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write,
|
||||
int old_val = *valp;
|
||||
int rc;
|
||||
|
||||
rc = mutex_lock_killable(&mac_hid_emumouse_mutex);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = proc_dointvec(table, write, buffer, lenp, ppos);
|
||||
|
||||
if (rc == 0 && write && *valp != old_val) {
|
||||
@ -202,6 +208,8 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write,
|
||||
if (rc)
|
||||
*valp = old_val;
|
||||
|
||||
mutex_unlock(&mac_hid_emumouse_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -112,6 +112,7 @@ struct input_keymap_entry {
|
||||
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
|
||||
#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
|
||||
#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
|
||||
#define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) /* get device properties */
|
||||
|
||||
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global key state */
|
||||
#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
|
||||
@ -128,6 +129,18 @@ struct input_keymap_entry {
|
||||
|
||||
#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
|
||||
|
||||
/*
|
||||
* Device properties and quirks
|
||||
*/
|
||||
|
||||
#define INPUT_PROP_POINTER 0x00 /* needs a pointer */
|
||||
#define INPUT_PROP_DIRECT 0x01 /* direct input devices */
|
||||
#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */
|
||||
#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */
|
||||
|
||||
#define INPUT_PROP_MAX 0x1f
|
||||
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
|
||||
|
||||
/*
|
||||
* Event types
|
||||
*/
|
||||
@ -760,11 +773,12 @@ struct input_keymap_entry {
|
||||
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
|
||||
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
|
||||
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
|
||||
#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* Implementation details, userspace should not care about these */
|
||||
#define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR
|
||||
#define ABS_MT_LAST ABS_MT_PRESSURE
|
||||
#define ABS_MT_LAST ABS_MT_DISTANCE
|
||||
#endif
|
||||
|
||||
#define ABS_MAX 0x3f
|
||||
@ -875,6 +889,7 @@ struct input_keymap_entry {
|
||||
*/
|
||||
#define MT_TOOL_FINGER 0
|
||||
#define MT_TOOL_PEN 1
|
||||
#define MT_TOOL_MAX 1
|
||||
|
||||
/*
|
||||
* Values describing the status of a force-feedback effect
|
||||
@ -1109,20 +1124,13 @@ struct ff_effect {
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
/**
|
||||
* struct input_mt_slot - represents the state of an input MT slot
|
||||
* @abs: holds current values of ABS_MT axes for this slot
|
||||
*/
|
||||
struct input_mt_slot {
|
||||
int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct input_dev - represents an input device
|
||||
* @name: name of the device
|
||||
* @phys: physical path to the device in the system hierarchy
|
||||
* @uniq: unique identification code for the device (if device has it)
|
||||
* @id: id of the device (struct input_id)
|
||||
* @propbit: bitmap of device properties and quirks
|
||||
* @evbit: bitmap of types of events supported by the device (EV_KEY,
|
||||
* EV_REL, etc.)
|
||||
* @keybit: bitmap of keys/buttons this device has
|
||||
@ -1157,6 +1165,7 @@ struct input_mt_slot {
|
||||
* of tracked contacts
|
||||
* @mtsize: number of MT slots the device uses
|
||||
* @slot: MT slot currently being transmitted
|
||||
* @trkid: stores MT tracking ID for the current contact
|
||||
* @absinfo: array of &struct input_absinfo elements holding information
|
||||
* about absolute axes (current value, min, max, flat, fuzz,
|
||||
* resolution)
|
||||
@ -1205,6 +1214,8 @@ struct input_dev {
|
||||
const char *uniq;
|
||||
struct input_id id;
|
||||
|
||||
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
|
||||
|
||||
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
|
||||
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
|
||||
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
|
||||
@ -1241,6 +1252,7 @@ struct input_dev {
|
||||
struct input_mt_slot *mt;
|
||||
int mtsize;
|
||||
int slot;
|
||||
int trkid;
|
||||
|
||||
struct input_absinfo *absinfo;
|
||||
|
||||
@ -1490,11 +1502,6 @@ static inline void input_mt_sync(struct input_dev *dev)
|
||||
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
|
||||
}
|
||||
|
||||
static inline void input_mt_slot(struct input_dev *dev, int slot)
|
||||
{
|
||||
input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
|
||||
}
|
||||
|
||||
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
|
||||
|
||||
/**
|
||||
@ -1607,8 +1614,5 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
|
||||
int input_ff_create_memless(struct input_dev *dev, void *data,
|
||||
int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
|
||||
|
||||
int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots);
|
||||
void input_mt_destroy_slots(struct input_dev *dev);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
59
include/linux/input/cma3000.h
Normal file
59
include/linux/input/cma3000.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* VTI CMA3000_Dxx Accelerometer driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Hemanth V <hemanthv@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_CMA3000_H
|
||||
#define _LINUX_CMA3000_H
|
||||
|
||||
#define CMAMODE_DEFAULT 0
|
||||
#define CMAMODE_MEAS100 1
|
||||
#define CMAMODE_MEAS400 2
|
||||
#define CMAMODE_MEAS40 3
|
||||
#define CMAMODE_MOTDET 4
|
||||
#define CMAMODE_FF100 5
|
||||
#define CMAMODE_FF400 6
|
||||
#define CMAMODE_POFF 7
|
||||
|
||||
#define CMARANGE_2G 2000
|
||||
#define CMARANGE_8G 8000
|
||||
|
||||
/**
|
||||
* struct cma3000_i2c_platform_data - CMA3000 Platform data
|
||||
* @fuzz_x: Noise on X Axis
|
||||
* @fuzz_y: Noise on Y Axis
|
||||
* @fuzz_z: Noise on Z Axis
|
||||
* @g_range: G range in milli g i.e 2000 or 8000
|
||||
* @mode: Operating mode
|
||||
* @mdthr: Motion detect threshold value
|
||||
* @mdfftmr: Motion detect and free fall time value
|
||||
* @ffthr: Free fall threshold value
|
||||
*/
|
||||
|
||||
struct cma3000_platform_data {
|
||||
int fuzz_x;
|
||||
int fuzz_y;
|
||||
int fuzz_z;
|
||||
int g_range;
|
||||
uint8_t mode;
|
||||
uint8_t mdthr;
|
||||
uint8_t mdfftmr;
|
||||
uint8_t ffthr;
|
||||
unsigned long irqflags;
|
||||
};
|
||||
|
||||
#endif
|
57
include/linux/input/mt.h
Normal file
57
include/linux/input/mt.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef _INPUT_MT_H
|
||||
#define _INPUT_MT_H
|
||||
|
||||
/*
|
||||
* Input Multitouch Library
|
||||
*
|
||||
* Copyright (c) 2010 Henrik Rydberg
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#define TRKID_MAX 0xffff
|
||||
|
||||
/**
|
||||
* struct input_mt_slot - represents the state of an input MT slot
|
||||
* @abs: holds current values of ABS_MT axes for this slot
|
||||
*/
|
||||
struct input_mt_slot {
|
||||
int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];
|
||||
};
|
||||
|
||||
static inline void input_mt_set_value(struct input_mt_slot *slot,
|
||||
unsigned code, int value)
|
||||
{
|
||||
slot->abs[code - ABS_MT_FIRST] = value;
|
||||
}
|
||||
|
||||
static inline int input_mt_get_value(const struct input_mt_slot *slot,
|
||||
unsigned code)
|
||||
{
|
||||
return slot->abs[code - ABS_MT_FIRST];
|
||||
}
|
||||
|
||||
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots);
|
||||
void input_mt_destroy_slots(struct input_dev *dev);
|
||||
|
||||
static inline int input_mt_new_trkid(struct input_dev *dev)
|
||||
{
|
||||
return dev->trkid++ & TRKID_MAX;
|
||||
}
|
||||
|
||||
static inline void input_mt_slot(struct input_dev *dev, int slot)
|
||||
{
|
||||
input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
|
||||
}
|
||||
|
||||
void input_mt_report_slot_state(struct input_dev *dev,
|
||||
unsigned int tool_type, bool active);
|
||||
|
||||
void input_mt_report_finger_count(struct input_dev *dev, int count);
|
||||
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
|
||||
|
||||
#endif
|
@ -104,6 +104,7 @@ struct uinput_ff_erase {
|
||||
#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
|
||||
#define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*)
|
||||
#define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int)
|
||||
#define UI_SET_PROPBIT _IOW(UINPUT_IOCTL_BASE, 110, int)
|
||||
|
||||
#define UI_BEGIN_FF_UPLOAD _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload)
|
||||
#define UI_END_FF_UPLOAD _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload)
|
||||
|
Loading…
Reference in New Issue
Block a user