Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+396
View File
@@ -0,0 +1,396 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Mouse driver configuration
#
menuconfig INPUT_MOUSE
bool "Mice"
default y
help
Say Y here, and a list of supported mice will be displayed.
This option doesn't affect the kernel.
If unsure, say Y.
if INPUT_MOUSE
config MOUSE_PS2
tristate "PS/2 mouse"
default y
select SERIO
select SERIO_LIBPS2
select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO
select SERIO_GSCPS2 if GSC
help
Say Y here if you have a PS/2 mouse connected to your system. This
includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
mice with wheels and extra buttons, Microsoft, Logitech or Genius
compatible.
Synaptics, ALPS or Elantech TouchPad users might be interested
in a specialized Xorg/XFree86 driver at:
<http://w1.894.telia.com/~u89404340/touchpad/index.html>
and a new version of GPM at:
<http://www.geocities.com/dt_or/gpm/gpm.html>
<http://xorg.freedesktop.org/archive/individual/driver/>
to take advantage of the advanced features of the touchpad.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called psmouse.
config MOUSE_PS2_ALPS
bool "ALPS PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have an ALPS PS/2 touchpad connected to
your system.
If unsure, say Y.
config MOUSE_PS2_BYD
bool "BYD PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a BYD PS/2 touchpad connected to
your system.
If unsure, say Y.
config MOUSE_PS2_LOGIPS2PP
bool "Logitech PS/2++ mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a Logitech PS/2++ mouse connected to
your system.
If unsure, say Y.
config MOUSE_PS2_SYNAPTICS
bool "Synaptics PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a Synaptics PS/2 TouchPad connected to
your system.
If unsure, say Y.
config MOUSE_PS2_SYNAPTICS_SMBUS
bool "Synaptics PS/2 SMbus companion" if EXPERT
default y
depends on MOUSE_PS2
depends on I2C=y || I2C=MOUSE_PS2
select MOUSE_PS2_SMBUS
help
Say Y here if you have a Synaptics RMI4 touchpad connected to
to an SMBus, but enumerated through PS/2.
If unsure, say Y.
config MOUSE_PS2_CYPRESS
bool "Cypress PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a Cypress PS/2 Trackpad connected to
your system.
If unsure, say Y.
config MOUSE_PS2_LIFEBOOK
bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2 && X86 && DMI
help
Say Y here if you have a Fujitsu B-series Lifebook PS/2
TouchScreen connected to your system.
If unsure, say Y.
config MOUSE_PS2_TRACKPOINT
bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have an IBM Trackpoint PS/2 mouse connected
to your system.
If unsure, say Y.
config MOUSE_PS2_ELANTECH
bool "Elantech PS/2 protocol extension"
depends on MOUSE_PS2
help
Say Y here if you have an Elantech PS/2 touchpad connected
to your system.
This driver exposes some configuration registers via sysfs
entries. For further information,
see <file:Documentation/input/devices/elantech.rst>.
If unsure, say N.
config MOUSE_PS2_ELANTECH_SMBUS
bool "Elantech PS/2 SMbus companion" if EXPERT
default y
depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
depends on I2C=y || I2C=MOUSE_PS2
select MOUSE_PS2_SMBUS
help
Say Y here if you have a Elantech touchpad connected to
to an SMBus, but enumerated through PS/2.
If unsure, say Y.
config MOUSE_PS2_SENTELIC
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
depends on MOUSE_PS2
help
Say Y here if you have a laptop (such as MSI WIND Netbook)
with Sentelic Finger Sensing Pad touchpad.
If unsure, say N.
config MOUSE_PS2_TOUCHKIT
bool "eGalax TouchKit PS/2 protocol extension"
depends on MOUSE_PS2
help
Say Y here if you have an eGalax TouchKit PS/2 touchscreen
connected to your system.
If unsure, say N.
config MOUSE_PS2_FOCALTECH
bool "FocalTech PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a FocalTech PS/2 TouchPad connected to
your system.
If unsure, say Y.
config MOUSE_PS2_VMMOUSE
bool "Virtual mouse (vmmouse)"
depends on MOUSE_PS2 && X86 && HYPERVISOR_GUEST
help
Say Y here if you are running under control of VMware hypervisor
(ESXi, Workstation or Fusion). Also make sure that when you enable
this option, you remove the xf86-input-vmmouse user-space driver
or upgrade it to at least xf86-input-vmmouse 13.1.0, which doesn't
load in the presence of an in-kernel vmmouse driver.
If unsure, say N.
config MOUSE_PS2_SMBUS
bool
depends on MOUSE_PS2
config MOUSE_SERIAL
tristate "Serial mouse"
select SERIO
help
Say Y here if you have a serial (RS-232, COM port) mouse connected
to your system. This includes Sun, MouseSystems, Microsoft,
Logitech and all other compatible serial mice.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called sermouse.
config MOUSE_APPLETOUCH
tristate "Apple USB Touchpad support"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use an Apple USB Touchpad.
These are the touchpads that can be found on post-February 2005
Apple Powerbooks (prior models have a Synaptics touchpad connected
to the ADB bus).
This driver provides a basic mouse driver but can be interfaced
with the synaptics X11 driver to provide acceleration and
scrolling in X11.
For further information, see
<file:Documentation/input/devices/appletouch.rst>.
To compile this driver as a module, choose M here: the
module will be called appletouch.
config MOUSE_BCM5974
tristate "Apple USB BCM5974 Multitouch trackpad support"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you have an Apple USB BCM5974 Multitouch
trackpad.
The BCM5974 is the multitouch trackpad found in the Macbook
Air (JAN2008) and Macbook Pro Penryn (FEB2008) laptops.
It is also found in the IPhone (2007) and Ipod Touch (2008).
This driver provides multitouch functionality together with
the synaptics X11 driver.
The interface is currently identical to the appletouch interface,
for further information, see
<file:Documentation/input/devices/appletouch.rst>.
To compile this driver as a module, choose M here: the
module will be called bcm5974.
config MOUSE_CYAPA
tristate "Cypress APA I2C Trackpad support"
depends on I2C
select CRC_ITU_T
help
This driver adds support for Cypress All Points Addressable (APA)
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
Say Y here if you have a Cypress APA I2C Trackpad.
To compile this driver as a module, choose M here: the module will be
called cyapa.
config MOUSE_ELAN_I2C
tristate "ELAN I2C Touchpad support"
depends on I2C
help
This driver adds support for Elan I2C/SMbus Trackpads.
Say Y here if you have a ELAN I2C/SMbus Touchpad.
To compile this driver as a module, choose M here: the module will be
called elan_i2c.
config MOUSE_ELAN_I2C_I2C
bool "Enable I2C support"
depends on MOUSE_ELAN_I2C
default y
help
Say Y here if Elan Touchpad in your system is connected to
a standard I2C controller.
If unsure, say Y.
config MOUSE_ELAN_I2C_SMBUS
bool "Enable SMbus support"
depends on MOUSE_ELAN_I2C
help
Say Y here if Elan Touchpad in your system is connected to
a SMbus adapter.
If unsure, say Y.
config MOUSE_AMIGA
tristate "Amiga mouse"
depends on AMIGA
help
Say Y here if you have an Amiga and want its native mouse
supported by the kernel.
To compile this driver as a module, choose M here: the
module will be called amimouse.
config MOUSE_ATARI
tristate "Atari mouse"
depends on ATARI
select ATARI_KBD_CORE
help
Say Y here if you have an Atari and want its native mouse
supported by the kernel.
To compile this driver as a module, choose M here: the
module will be called atarimouse.
config MOUSE_RISCPC
tristate "Acorn RiscPC mouse"
depends on ARCH_ACORN
help
Say Y here if you have the Acorn RiscPC computer and want its
native mouse supported.
To compile this driver as a module, choose M here: the
module will be called rpcmouse.
config MOUSE_VSXXXAA
tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet"
select SERIO
help
Say Y (or M) if you want to use a DEC VSXXX-AA (hockey
puck) or a VSXXX-GA (rectangular) mouse. These mice are
typically used on DECstations or VAXstations, but can also
be used on any box capable of RS232 (with some adaptor
described in the source file). This driver also works with the
digitizer (VSXXX-AB) DEC produced.
config MOUSE_GPIO
tristate "GPIO mouse"
depends on GPIOLIB || COMPILE_TEST
help
This driver simulates a mouse on GPIO lines of various CPUs (and some
other chips).
Say Y here if your device has buttons or a simple joystick connected
directly to GPIO lines. Your board-specific setup logic must also
provide a platform device and platform data saying which GPIOs are
used.
To compile this driver as a module, choose M here: the
module will be called gpio_mouse.
config MOUSE_MAPLE
tristate "Maple mouse (for the Dreamcast)"
depends on MAPLE
help
This driver supports the Maple mouse on the SEGA Dreamcast.
Most Dreamcast users, who have a mouse, will say Y here.
To compile this driver as a module choose M here: the module will be
called maplemouse.
config MOUSE_SYNAPTICS_I2C
tristate "Synaptics I2C Touchpad support"
depends on I2C
help
This driver supports Synaptics I2C touchpad controller on eXeda
mobile device.
The device will not work the synaptics X11 driver because
(i) it reports only relative coordinates and has no capabilities
to report absolute coordinates
(ii) the eXeda device itself uses Xfbdev as X Server and it does
not allow using xf86-input-* drivers.
Say y here if you have eXeda device and want to use a Synaptics
I2C Touchpad.
To compile this driver as a module, choose M here: the
module will be called synaptics_i2c.
config MOUSE_SYNAPTICS_USB
tristate "Synaptics USB device support"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use a Synaptics USB touchpad or pointing
stick.
While these devices emulate an USB mouse by default and can be used
with standard usbhid driver, this driver, together with its X.Org
counterpart, allows you to fully utilize capabilities of the device.
More information can be found at:
<http://jan-steinhoff.de/linux/synaptics-usb.html>
To compile this driver as a module, choose M here: the
module will be called synaptics_usb.
endif
+41
View File
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the mouse drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o
obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o
psmouse-objs := psmouse-base.o synaptics.o focaltech.o
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o
psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o
psmouse-$(CONFIG_MOUSE_PS2_SMBUS) += psmouse-smbus.o
elan_i2c-objs := elan_i2c_core.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o
elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS) += elan_i2c_smbus.o
File diff suppressed because it is too large Load Diff
+329
View File
@@ -0,0 +1,329 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALPS touchpad PS/2 mouse driver
*
* Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
*/
#ifndef _ALPS_H
#define _ALPS_H
#include <linux/input/mt.h>
#define ALPS_PROTO_V1 0x100
#define ALPS_PROTO_V2 0x200
#define ALPS_PROTO_V3 0x300
#define ALPS_PROTO_V3_RUSHMORE 0x310
#define ALPS_PROTO_V4 0x400
#define ALPS_PROTO_V5 0x500
#define ALPS_PROTO_V6 0x600
#define ALPS_PROTO_V7 0x700 /* t3btl t4s */
#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */
#define ALPS_PROTO_V9 0x900 /* ss3btl */
#define MAX_TOUCHES 4
#define DOLPHIN_COUNT_PER_ELECTRODE 64
#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */
#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */
/*
* enum SS4_PACKET_ID - defines the packet type for V8
* SS4_PACKET_ID_IDLE: There's no finger and no button activity.
* SS4_PACKET_ID_ONE: There's one finger on touchpad
* or there's button activities.
* SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
* SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
* SS4_PACKET_ID_STICK: A stick pointer packet
*/
enum SS4_PACKET_ID {
SS4_PACKET_ID_IDLE = 0,
SS4_PACKET_ID_ONE,
SS4_PACKET_ID_TWO,
SS4_PACKET_ID_MULTI,
SS4_PACKET_ID_STICK,
};
#define SS4_COUNT_PER_ELECTRODE 256
#define SS4_NUMSENSOR_XOFFSET 7
#define SS4_NUMSENSOR_YOFFSET 7
#define SS4_MIN_PITCH_MM 50
#define SS4_MASK_NORMAL_BUTTONS 0x07
#define SS4PLUS_COUNT_PER_ELECTRODE 128
#define SS4PLUS_NUMSENSOR_XOFFSET 16
#define SS4PLUS_NUMSENSOR_YOFFSET 5
#define SS4PLUS_MIN_PITCH_MM 37
#define IS_SS4PLUS_DEV(_b) (((_b[0]) == 0x73) && \
((_b[1]) == 0x03) && \
((_b[2]) == 0x28) \
)
#define SS4_IS_IDLE_V2(_b) (((_b[0]) == 0x18) && \
((_b[1]) == 0x10) && \
((_b[2]) == 0x00) && \
((_b[3] & 0x88) == 0x08) && \
((_b[4]) == 0x10) && \
((_b[5]) == 0x00) \
)
#define SS4_1F_X_V2(_b) (((_b[0]) & 0x0007) | \
((_b[1] << 3) & 0x0078) | \
((_b[1] << 2) & 0x0380) | \
((_b[2] << 5) & 0x1C00) \
)
#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \
((_b[3] >> 2) & 0x0030) | \
((_b[4] << 6) & 0x03C0) | \
((_b[4] << 5) & 0x0C00) \
)
#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \
((_b[5] >> 1) & 0x70) | \
((_b[4]) & 0x80) \
)
#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01)
#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004)
#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS)
#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \
((_b[1 + _i * 3] << 5) & 0x1F00) \
)
#define SS4_PLUS_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 4) & 0x0070) | \
((_b[1 + (_i) * 3] << 4) & 0x0F80) \
)
#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \
((_b[2 + (_i) * 3] << 5) & 0x01E0) | \
((_b[2 + (_i) * 3] << 4) & 0x0E00) \
)
#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \
((_b[0 + (_i) * 3] >> 3) & 0x0010) \
)
#define SS4_PLUS_BTL_MF_X_V2(_b, _i) (SS4_PLUS_STD_MF_X_V2(_b, _i) | \
((_b[0 + (_i) * 3] >> 4) & 0x0008) \
)
#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \
((_b[0 + (_i) * 3] >> 3) & 0x0008) \
)
#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \
((_b[1 + (_i) * 3] >> 1) & 0x0002) \
)
#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10)
#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10)
#define SS4_TS_X_V2(_b) (s8)( \
((_b[0] & 0x01) << 7) | \
(_b[1] & 0x7F) \
)
#define SS4_TS_Y_V2(_b) -(s8)( \
((_b[3] & 0x01) << 7) | \
(_b[2] & 0x7F) \
)
#define SS4_TS_Z_V2(_b) (s8)(_b[4] & 0x7F)
#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */
#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */
#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coord value */
#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coord value */
#define SS4_PLUS_MFPACKET_NO_AX 4080 /* SS4 PLUS, X */
#define SS4_PLUS_MFPACKET_NO_AX_BL 4088 /* Buttonless SS4 PLUS, X */
/*
* enum V7_PACKET_ID - defines the packet type for V7
* V7_PACKET_ID_IDLE: There's no finger and no button activity.
* V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
* or there's button activities.
* V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
* V7_PACKET_ID_NEW: The finger position in slot is not continues from
* previous packet.
*/
enum V7_PACKET_ID {
V7_PACKET_ID_IDLE,
V7_PACKET_ID_TWO,
V7_PACKET_ID_MULTI,
V7_PACKET_ID_NEW,
V7_PACKET_ID_UNKNOWN,
};
/**
* struct alps_protocol_info - information about protocol used by a device
* @version: Indicates V1/V2/V3/...
* @byte0: Helps figure out whether a position report packet matches the
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
*/
struct alps_protocol_info {
u16 version;
u8 byte0, mask0;
unsigned int flags;
};
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
* @protocol_info: information about protocol used by the device.
*
* Many (but not all) ALPS touchpads can be identified by looking at the
* values returned in the "E7 report" and/or the "EC report." This table
* lists a number of such touchpads.
*/
struct alps_model_info {
u8 signature[3];
struct alps_protocol_info protocol_info;
};
/**
* struct alps_nibble_commands - encodings for register accesses
* @command: PS/2 command used for the nibble
* @data: Data supplied as an argument to the PS/2 command, if applicable
*
* The ALPS protocol uses magic sequences to transmit binary data to the
* touchpad, as it is generally not OK to send arbitrary bytes out the
* PS/2 port. Each of the sequences in this table sends one nibble of the
* register address or (write) data. Different versions of the ALPS protocol
* use slightly different encodings.
*/
struct alps_nibble_commands {
int command;
unsigned char data;
};
struct alps_bitmap_point {
int start_bit;
int num_bits;
};
/**
* struct alps_fields - decoded version of the report packet
* @x_map: Bitmap of active X positions for MT.
* @y_map: Bitmap of active Y positions for MT.
* @fingers: Number of fingers for MT.
* @pressure: Pressure.
* @st: position for ST.
* @mt: position for MT.
* @first_mp: Packet is the first of a multi-packet report.
* @is_mp: Packet is part of a multi-packet report.
* @left: Left touchpad button is active.
* @right: Right touchpad button is active.
* @middle: Middle touchpad button is active.
* @ts_left: Left trackstick button is active.
* @ts_right: Right trackstick button is active.
* @ts_middle: Middle trackstick button is active.
*/
struct alps_fields {
unsigned int x_map;
unsigned int y_map;
unsigned int fingers;
int pressure;
struct input_mt_pos st;
struct input_mt_pos mt[MAX_TOUCHES];
unsigned int first_mp:1;
unsigned int is_mp:1;
unsigned int left:1;
unsigned int right:1;
unsigned int middle:1;
unsigned int ts_left:1;
unsigned int ts_right:1;
unsigned int ts_middle:1;
};
/**
* struct alps_data - private data structure for the ALPS driver
* @psmouse: Pointer to parent psmouse device
* @dev2: Trackstick device (can be NULL).
* @dev3: Generic PS/2 mouse (can be NULL, delayed registering).
* @phys2: Physical path for the trackstick device.
* @phys3: Physical path for the generic PS/2 mouse.
* @dev3_register_work: A work instance for registering PS/2 mouse.
* @nibble_commands: Command mapping used for touchpad register accesses.
* @addr_command: Command used to tell the touchpad that a register address
* follows.
* @proto_version: Indicates V1/V2/V3/...
* @byte0: Helps figure out whether a position report packet matches the
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
* @fw_ver: cached copy of firmware version (EC report)
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
* @x_max: Largest possible X position value.
* @y_max: Largest possible Y position value.
* @x_bits: Number of X bits in the MT bitmap.
* @y_bits: Number of Y bits in the MT bitmap.
* @hw_init: Protocol-specific hardware init function.
* @process_packet: Protocol-specific function to process a report packet.
* @decode_fields: Protocol-specific function to read packet bitfields.
* @set_abs_params: Protocol-specific function to configure the input_dev.
* @prev_fin: Finger bit from previous packet.
* @multi_packet: Multi-packet data in progress.
* @multi_data: Saved multi-packet data.
* @f: Decoded packet data fields.
* @quirks: Bitmap of ALPS_QUIRK_*.
* @timer: Timer for flushing out the final report packet in the stream.
*/
struct alps_data {
struct psmouse *psmouse;
struct input_dev *dev2;
struct input_dev *dev3;
char phys2[32];
char phys3[32];
struct work_struct dev3_register_work;
/* these are autodetected when the device is identified */
const struct alps_nibble_commands *nibble_commands;
int addr_command;
u16 proto_version;
u8 byte0, mask0;
u8 dev_id[3];
u8 fw_ver[3];
int flags;
int x_max;
int y_max;
int x_bits;
int y_bits;
unsigned int x_res;
unsigned int y_res;
int (*hw_init)(struct psmouse *psmouse);
void (*process_packet)(struct psmouse *psmouse);
int (*decode_fields)(struct alps_fields *f, unsigned char *p,
struct psmouse *psmouse);
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
int prev_fin;
int multi_packet;
int second_touch;
unsigned char multi_data[6];
struct alps_fields f;
u8 quirks;
struct timer_list timer;
};
#define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */
int alps_detect(struct psmouse *psmouse, bool set_properties);
int alps_init(struct psmouse *psmouse);
#endif
+150
View File
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Amiga mouse driver for Linux/m68k
*
* Copyright (c) 2000-2002 Vojtech Pavlik
*
* Based on the work of:
* Michael Rausch James Banks
* Matther Dillon David Giller
* Nathan Laredo Linus Torvalds
* Johan Myreen Jes Sorensen
* Russell King
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <linux/uaccess.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Amiga mouse driver");
MODULE_LICENSE("GPL");
static int amimouse_lastx, amimouse_lasty;
static irqreturn_t amimouse_interrupt(int irq, void *data)
{
struct input_dev *dev = data;
unsigned short joy0dat, potgor;
int nx, ny, dx, dy;
joy0dat = amiga_custom.joy0dat;
nx = joy0dat & 0xff;
ny = joy0dat >> 8;
dx = nx - amimouse_lastx;
dy = ny - amimouse_lasty;
if (dx < -127) dx = (256 + nx) - amimouse_lastx;
if (dx > 127) dx = (nx - 256) - amimouse_lastx;
if (dy < -127) dy = (256 + ny) - amimouse_lasty;
if (dy > 127) dy = (ny - 256) - amimouse_lasty;
amimouse_lastx = nx;
amimouse_lasty = ny;
potgor = amiga_custom.potgor;
input_report_rel(dev, REL_X, dx);
input_report_rel(dev, REL_Y, dy);
input_report_key(dev, BTN_LEFT, ciaa.pra & 0x40);
input_report_key(dev, BTN_MIDDLE, potgor & 0x0100);
input_report_key(dev, BTN_RIGHT, potgor & 0x0400);
input_sync(dev);
return IRQ_HANDLED;
}
static int amimouse_open(struct input_dev *dev)
{
unsigned short joy0dat;
int error;
joy0dat = amiga_custom.joy0dat;
amimouse_lastx = joy0dat & 0xff;
amimouse_lasty = joy0dat >> 8;
error = request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse",
dev);
if (error)
dev_err(&dev->dev, "Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
return error;
}
static void amimouse_close(struct input_dev *dev)
{
free_irq(IRQ_AMIGA_VERTB, dev);
}
static int __init amimouse_probe(struct platform_device *pdev)
{
int err;
struct input_dev *dev;
dev = input_allocate_device();
if (!dev)
return -ENOMEM;
dev->name = pdev->name;
dev->phys = "amimouse/input0";
dev->id.bustype = BUS_AMIGA;
dev->id.vendor = 0x0001;
dev->id.product = 0x0002;
dev->id.version = 0x0100;
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
dev->open = amimouse_open;
dev->close = amimouse_close;
dev->dev.parent = &pdev->dev;
err = input_register_device(dev);
if (err) {
input_free_device(dev);
return err;
}
platform_set_drvdata(pdev, dev);
return 0;
}
static void __exit amimouse_remove(struct platform_device *pdev)
{
struct input_dev *dev = platform_get_drvdata(pdev);
input_unregister_device(dev);
}
/*
* amimouse_remove() lives in .exit.text. For drivers registered via
* module_platform_driver_probe() this is ok because they cannot get unbound at
* runtime. So mark the driver struct with __refdata to prevent modpost
* triggering a section mismatch warning.
*/
static struct platform_driver amimouse_driver __refdata = {
.remove = __exit_p(amimouse_remove),
.driver = {
.name = "amiga-mouse",
},
};
module_platform_driver_probe(amimouse_driver, amimouse_probe);
MODULE_ALIAS("platform:amiga-mouse");
@@ -0,0 +1,997 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net)
* Copyright (C) 2005-2008 Stelian Pop (stelian@popies.net)
* Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
* Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
* Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
* Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
* Copyright (C) 2007-2008 Sven Anders (anders@anduras.de)
*
* Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
/*
* Note: We try to keep the touchpad aspect ratio while still doing only
* simple arithmetics:
* 0 <= x <= (xsensors - 1) * xfact
* 0 <= y <= (ysensors - 1) * yfact
*/
struct atp_info {
int xsensors; /* number of X sensors */
int xsensors_17; /* 17" models have more sensors */
int ysensors; /* number of Y sensors */
int xfact; /* X multiplication factor */
int yfact; /* Y multiplication factor */
int datalen; /* size of USB transfers */
void (*callback)(struct urb *); /* callback function */
int fuzz; /* fuzz touchpad generates */
};
static void atp_complete_geyser_1_2(struct urb *urb);
static void atp_complete_geyser_3_4(struct urb *urb);
static const struct atp_info fountain_info = {
.xsensors = 16,
.xsensors_17 = 26,
.ysensors = 16,
.xfact = 64,
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
.fuzz = 16,
};
static const struct atp_info geyser1_info = {
.xsensors = 16,
.xsensors_17 = 26,
.ysensors = 16,
.xfact = 64,
.yfact = 43,
.datalen = 81,
.callback = atp_complete_geyser_1_2,
.fuzz = 16,
};
static const struct atp_info geyser2_info = {
.xsensors = 15,
.xsensors_17 = 20,
.ysensors = 9,
.xfact = 64,
.yfact = 43,
.datalen = 64,
.callback = atp_complete_geyser_1_2,
.fuzz = 0,
};
static const struct atp_info geyser3_info = {
.xsensors = 20,
.ysensors = 10,
.xfact = 64,
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
.fuzz = 0,
};
static const struct atp_info geyser4_info = {
.xsensors = 20,
.ysensors = 10,
.xfact = 64,
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
.fuzz = 0,
};
#define ATP_DEVICE(prod, info) \
{ \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
.idVendor = 0x05ac, /* Apple */ \
.idProduct = (prod), \
.bInterfaceClass = 0x03, \
.bInterfaceProtocol = 0x02, \
.driver_info = (unsigned long) &info, \
}
/*
* Table of devices (Product IDs) that work with this driver.
* (The names come from Info.plist in AppleUSBTrackpad.kext,
* According to Info.plist Geyser IV is the same as Geyser III.)
*/
static const struct usb_device_id atp_table[] = {
/* PowerBooks Feb 2005, iBooks G4 */
ATP_DEVICE(0x020e, fountain_info), /* FOUNTAIN ANSI */
ATP_DEVICE(0x020f, fountain_info), /* FOUNTAIN ISO */
ATP_DEVICE(0x030a, fountain_info), /* FOUNTAIN TP ONLY */
ATP_DEVICE(0x030b, geyser1_info), /* GEYSER 1 TP ONLY */
/* PowerBooks Oct 2005 */
ATP_DEVICE(0x0214, geyser2_info), /* GEYSER 2 ANSI */
ATP_DEVICE(0x0215, geyser2_info), /* GEYSER 2 ISO */
ATP_DEVICE(0x0216, geyser2_info), /* GEYSER 2 JIS */
/* Core Duo MacBook & MacBook Pro */
ATP_DEVICE(0x0217, geyser3_info), /* GEYSER 3 ANSI */
ATP_DEVICE(0x0218, geyser3_info), /* GEYSER 3 ISO */
ATP_DEVICE(0x0219, geyser3_info), /* GEYSER 3 JIS */
/* Core2 Duo MacBook & MacBook Pro */
ATP_DEVICE(0x021a, geyser4_info), /* GEYSER 4 ANSI */
ATP_DEVICE(0x021b, geyser4_info), /* GEYSER 4 ISO */
ATP_DEVICE(0x021c, geyser4_info), /* GEYSER 4 JIS */
/* Core2 Duo MacBook3,1 */
ATP_DEVICE(0x0229, geyser4_info), /* GEYSER 4 HF ANSI */
ATP_DEVICE(0x022a, geyser4_info), /* GEYSER 4 HF ISO */
ATP_DEVICE(0x022b, geyser4_info), /* GEYSER 4 HF JIS */
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE(usb, atp_table);
/* maximum number of sensors */
#define ATP_XSENSORS 26
#define ATP_YSENSORS 16
/*
* The largest possible bank of sensors with additional buffer of 4 extra values
* on either side, for an array of smoothed sensor values.
*/
#define ATP_SMOOTHSIZE 34
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
/*
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
* ignored.
*/
#define ATP_THRESHOLD 5
/*
* How far we'll bitshift our sensor values before averaging them. Mitigates
* rounding errors.
*/
#define ATP_SCALE 12
/* Geyser initialization constants */
#define ATP_GEYSER_MODE_READ_REQUEST_ID 1
#define ATP_GEYSER_MODE_WRITE_REQUEST_ID 9
#define ATP_GEYSER_MODE_REQUEST_VALUE 0x300
#define ATP_GEYSER_MODE_REQUEST_INDEX 0
#define ATP_GEYSER_MODE_VENDOR_VALUE 0x04
/**
* enum atp_status_bits - status bit meanings
*
* These constants represent the meaning of the status bits.
* (only Geyser 3/4)
*
* @ATP_STATUS_BUTTON: The button was pressed
* @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad)
* @ATP_STATUS_FROM_RESET: Reset previously performed
*/
enum atp_status_bits {
ATP_STATUS_BUTTON = BIT(0),
ATP_STATUS_BASE_UPDATE = BIT(2),
ATP_STATUS_FROM_RESET = BIT(4),
};
/* Structure to hold all of our device specific stuff */
struct atp {
char phys[64];
struct usb_device *udev; /* usb device */
struct usb_interface *intf; /* usb interface */
struct urb *urb; /* usb request block */
u8 *data; /* transferred data */
struct input_dev *input; /* input dev */
const struct atp_info *info; /* touchpad model */
bool valid; /* are the samples valid? */
bool size_detect_done;
bool overflow_warned;
int fingers_old; /* last reported finger count */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
int smooth[ATP_SMOOTHSIZE];
int smooth_tmp[ATP_SMOOTHSIZE];
int idlecount; /* number of empty packets */
struct work_struct work;
};
#define dbg_dump(msg, tab) \
if (debug > 1) { \
int __i; \
printk(KERN_DEBUG "appletouch: %s", msg); \
for (__i = 0; __i < ATP_XSENSORS + ATP_YSENSORS; __i++) \
printk(" %02x", tab[__i]); \
printk("\n"); \
}
#define dprintk(format, a...) \
do { \
if (debug) \
printk(KERN_DEBUG format, ##a); \
} while (0)
MODULE_AUTHOR("Johannes Berg");
MODULE_AUTHOR("Stelian Pop");
MODULE_AUTHOR("Frank Arnold");
MODULE_AUTHOR("Michael Hanselmann");
MODULE_AUTHOR("Sven Anders");
MODULE_DESCRIPTION("Apple PowerBook and MacBook USB touchpad driver");
MODULE_LICENSE("GPL");
/*
* Make the threshold a module parameter
*/
static int threshold = ATP_THRESHOLD;
module_param(threshold, int, 0644);
MODULE_PARM_DESC(threshold, "Discard any change in data from a sensor"
" (the trackpad has many of these sensors)"
" less than this value.");
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");
/*
* By default newer Geyser devices send standard USB HID mouse
* packets (Report ID 2). This code changes device mode, so it
* sends raw sensor reports (Report ID 5).
*/
static int atp_geyser_init(struct atp *dev)
{
struct usb_device *udev = dev->udev;
char *data;
int size;
int i;
int ret;
data = kmalloc(8, GFP_KERNEL);
if (!data) {
dev_err(&dev->intf->dev, "Out of memory\n");
return -ENOMEM;
}
size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
ATP_GEYSER_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER_MODE_REQUEST_VALUE,
ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
if (size != 8) {
dprintk("atp_geyser_init: read error\n");
for (i = 0; i < 8; i++)
dprintk("appletouch[%d]: %d\n", i, data[i]);
dev_err(&dev->intf->dev, "Failed to read mode from device.\n");
ret = -EIO;
goto out_free;
}
/* Apply the mode switch */
data[0] = ATP_GEYSER_MODE_VENDOR_VALUE;
size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
ATP_GEYSER_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER_MODE_REQUEST_VALUE,
ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
if (size != 8) {
dprintk("atp_geyser_init: write error\n");
for (i = 0; i < 8; i++)
dprintk("appletouch[%d]: %d\n", i, data[i]);
dev_err(&dev->intf->dev, "Failed to request geyser raw mode\n");
ret = -EIO;
goto out_free;
}
ret = 0;
out_free:
kfree(data);
return ret;
}
/*
* Reinitialise the device. This usually stops stream of empty packets
* coming from it.
*/
static void atp_reinit(struct work_struct *work)
{
struct atp *dev = container_of(work, struct atp, work);
int retval;
dprintk("appletouch: putting appletouch to sleep (reinit)\n");
atp_geyser_init(dev);
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
if (retval)
dev_err(&dev->intf->dev,
"atp_reinit: usb_submit_urb failed with error %d\n",
retval);
}
static int atp_calculate_abs(struct atp *dev, int offset, int nb_sensors,
int fact, int *z, int *fingers)
{
int i, pass;
/*
* Use offset to point xy_sensors at the first value in dev->xy_acc
* for whichever dimension we're looking at this particular go-round.
*/
int *xy_sensors = dev->xy_acc + offset;
/* values to calculate mean */
int pcum = 0, psum = 0;
int is_increasing = 0;
*fingers = 0;
for (i = 0; i < nb_sensors; i++) {
if (xy_sensors[i] < threshold) {
if (is_increasing)
is_increasing = 0;
/*
* Makes the finger detection more versatile. For example,
* two fingers with no gap will be detected. Also, my
* tests show it less likely to have intermittent loss
* of multiple finger readings while moving around (scrolling).
*
* Changes the multiple finger detection to counting humps on
* sensors (transitions from nonincreasing to increasing)
* instead of counting transitions from low sensors (no
* finger reading) to high sensors (finger above
* sensor)
*
* - Jason Parekh <jasonparekh@gmail.com>
*/
} else if (i < 1 ||
(!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
(*fingers)++;
is_increasing = 1;
} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
is_increasing = 0;
}
}
if (*fingers < 1) /* No need to continue if no fingers are found. */
return 0;
/*
* Use a smoothed version of sensor data for movement calculations, to
* combat noise without needing to rely so heavily on a threshold.
* This improves tracking.
*
* The smoothed array is bigger than the original so that the smoothing
* doesn't result in edge values being truncated.
*/
memset(dev->smooth, 0, 4 * sizeof(dev->smooth[0]));
/* Pull base values, scaled up to help avoid truncation errors. */
for (i = 0; i < nb_sensors; i++)
dev->smooth[i + 4] = xy_sensors[i] << ATP_SCALE;
memset(&dev->smooth[nb_sensors + 4], 0, 4 * sizeof(dev->smooth[0]));
for (pass = 0; pass < 4; pass++) {
/* Handle edge. */
dev->smooth_tmp[0] = (dev->smooth[0] + dev->smooth[1]) / 2;
/* Average values with neighbors. */
for (i = 1; i < nb_sensors + 7; i++)
dev->smooth_tmp[i] = (dev->smooth[i - 1] +
dev->smooth[i] * 2 +
dev->smooth[i + 1]) / 4;
/* Handle other edge. */
dev->smooth_tmp[i] = (dev->smooth[i - 1] + dev->smooth[i]) / 2;
memcpy(dev->smooth, dev->smooth_tmp, sizeof(dev->smooth));
}
for (i = 0; i < nb_sensors + 8; i++) {
/*
* Skip values if they're small enough to be truncated to 0
* by scale. Mostly noise.
*/
if ((dev->smooth[i] >> ATP_SCALE) > 0) {
pcum += dev->smooth[i] * i;
psum += dev->smooth[i];
}
}
if (psum > 0) {
*z = psum >> ATP_SCALE; /* Scale down pressure output. */
return pcum * fact / psum;
}
return 0;
}
static inline void atp_report_fingers(struct input_dev *input, int fingers)
{
input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
}
/* Check URB status and for correct length of data package */
#define ATP_URB_STATUS_SUCCESS 0
#define ATP_URB_STATUS_ERROR 1
#define ATP_URB_STATUS_ERROR_FATAL 2
static int atp_status_check(struct urb *urb)
{
struct atp *dev = urb->context;
struct usb_interface *intf = dev->intf;
switch (urb->status) {
case 0:
/* success */
break;
case -EOVERFLOW:
if (!dev->overflow_warned) {
dev_warn(&intf->dev,
"appletouch: OVERFLOW with data length %d, actual length is %d\n",
dev->info->datalen, dev->urb->actual_length);
dev->overflow_warned = true;
}
fallthrough;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* This urb is terminated, clean up */
dev_dbg(&intf->dev,
"atp_complete: urb shutting down with status: %d\n",
urb->status);
return ATP_URB_STATUS_ERROR_FATAL;
default:
dev_dbg(&intf->dev,
"atp_complete: nonzero urb status received: %d\n",
urb->status);
return ATP_URB_STATUS_ERROR;
}
/* drop incomplete datasets */
if (dev->urb->actual_length != dev->info->datalen) {
dprintk("appletouch: incomplete data package"
" (first byte: %d, length: %d).\n",
dev->data[0], dev->urb->actual_length);
return ATP_URB_STATUS_ERROR;
}
return ATP_URB_STATUS_SUCCESS;
}
static void atp_detect_size(struct atp *dev)
{
int i;
/* 17" Powerbooks have extra X sensors */
for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {
if (dev->xy_cur[i]) {
dev_info(&dev->intf->dev,
"appletouch: 17\" model detected.\n");
input_set_abs_params(dev->input, ABS_X, 0,
(dev->info->xsensors_17 - 1) *
dev->info->xfact - 1,
dev->info->fuzz, 0);
break;
}
}
}
/*
* USB interrupt callback functions
*/
/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
static void atp_complete_geyser_1_2(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
if (status == ATP_URB_STATUS_ERROR_FATAL)
return;
else if (status == ATP_URB_STATUS_ERROR)
goto exit;
/* reorder the sensors values */
if (dev->info == &geyser2_info) {
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
/*
* The values are laid out like this:
* Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
* '-' is an unused value.
*/
/* read X values */
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
dev->xy_cur[i] = dev->data[j];
dev->xy_cur[i + 1] = dev->data[j + 1];
}
/* read Y values */
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
}
} else {
for (i = 0; i < 8; i++) {
/* X values */
dev->xy_cur[i + 0] = dev->data[5 * i + 2];
dev->xy_cur[i + 8] = dev->data[5 * i + 4];
dev->xy_cur[i + 16] = dev->data[5 * i + 42];
if (i < 2)
dev->xy_cur[i + 24] = dev->data[5 * i + 44];
/* Y values */
dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i + 1];
dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3];
}
}
dbg_dump("sample", dev->xy_cur);
if (!dev->valid) {
/* first sample */
dev->valid = true;
dev->x_old = dev->y_old = -1;
/* Store first sample */
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
/* Perform size detection, if not done already */
if (unlikely(!dev->size_detect_done)) {
atp_detect_size(dev);
dev->size_detect_done = true;
goto exit;
}
}
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
/* accumulate the change */
signed char change = dev->xy_old[i] - dev->xy_cur[i];
dev->xy_acc[i] -= change;
/* prevent down drifting */
if (dev->xy_acc[i] < 0)
dev->xy_acc[i] = 0;
}
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
dbg_dump("accumulator", dev->xy_acc);
x = atp_calculate_abs(dev, 0, ATP_XSENSORS,
dev->info->xfact, &x_z, &x_f);
y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
fingers = max(x_f, y_f);
if (x && y && fingers == dev->fingers_old) {
if (dev->x_old != -1) {
x = (dev->x_old * 7 + x) >> 3;
y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
if (debug > 1)
printk(KERN_DEBUG "appletouch: "
"X: %3d Y: %3d Xz: %3d Yz: %3d\n",
x, y, x_z, y_z);
input_report_key(dev->input, BTN_TOUCH, 1);
input_report_abs(dev->input, ABS_X, x);
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
if (fingers != dev->fingers_old)
dev->x_old = dev->y_old = -1;
dev->fingers_old = fingers;
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
exit:
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
if (retval)
dev_err(&dev->intf->dev,
"atp_complete: usb_submit_urb failed with result %d\n",
retval);
}
/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
static void atp_complete_geyser_3_4(struct urb *urb)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i, j;
int key, fingers;
struct atp *dev = urb->context;
int status = atp_status_check(urb);
if (status == ATP_URB_STATUS_ERROR_FATAL)
return;
else if (status == ATP_URB_STATUS_ERROR)
goto exit;
/* Reorder the sensors values:
*
* The values are laid out like this:
* -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
* '-' is an unused value.
*/
/* read X values */
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
dev->xy_cur[i] = dev->data[j + 1];
dev->xy_cur[i + 1] = dev->data[j + 2];
}
/* read Y values */
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
}
dbg_dump("sample", dev->xy_cur);
/* Just update the base values (i.e. touchpad in untouched state) */
if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
dprintk("appletouch: updated base values\n");
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
goto exit;
}
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
/* calculate the change */
dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
/* this is a round-robin value, so couple with that */
if (dev->xy_acc[i] > 127)
dev->xy_acc[i] -= 256;
if (dev->xy_acc[i] < -127)
dev->xy_acc[i] += 256;
/* prevent down drifting */
if (dev->xy_acc[i] < 0)
dev->xy_acc[i] = 0;
}
dbg_dump("accumulator", dev->xy_acc);
x = atp_calculate_abs(dev, 0, ATP_XSENSORS,
dev->info->xfact, &x_z, &x_f);
y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS,
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
fingers = max(x_f, y_f);
if (x && y && fingers == dev->fingers_old) {
if (dev->x_old != -1) {
x = (dev->x_old * 7 + x) >> 3;
y = (dev->y_old * 7 + y) >> 3;
dev->x_old = x;
dev->y_old = y;
if (debug > 1)
printk(KERN_DEBUG "appletouch: X: %3d Y: %3d "
"Xz: %3d Yz: %3d\n",
x, y, x_z, y_z);
input_report_key(dev->input, BTN_TOUCH, 1);
input_report_abs(dev->input, ABS_X, x);
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE,
min(ATP_PRESSURE, x_z + y_z));
atp_report_fingers(dev->input, fingers);
}
dev->x_old = x;
dev->y_old = y;
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
dev->fingers_old = 0;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
if (fingers != dev->fingers_old)
dev->x_old = dev->y_old = -1;
dev->fingers_old = fingers;
input_report_key(dev->input, BTN_LEFT, key);
input_sync(dev->input);
/*
* Geysers 3/4 will continue to send packets continually after
* the first touch unless reinitialised. Do so if it's been
* idle for a while in order to avoid waking the kernel up
* several hundred times a second.
*/
/*
* Button must not be pressed when entering suspend,
* otherwise we will never release the button.
*/
if (!x && !y && !key) {
dev->idlecount++;
if (dev->idlecount == 10) {
dev->x_old = dev->y_old = -1;
dev->idlecount = 0;
schedule_work(&dev->work);
/* Don't resubmit urb here, wait for reinit */
return;
}
} else
dev->idlecount = 0;
exit:
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
if (retval)
dev_err(&dev->intf->dev,
"atp_complete: usb_submit_urb failed with result %d\n",
retval);
}
static int atp_open(struct input_dev *input)
{
struct atp *dev = input_get_drvdata(input);
if (usb_submit_urb(dev->urb, GFP_KERNEL))
return -EIO;
return 0;
}
static void atp_close(struct input_dev *input)
{
struct atp *dev = input_get_drvdata(input);
usb_kill_urb(dev->urb);
cancel_work_sync(&dev->work);
}
static int atp_handle_geyser(struct atp *dev)
{
if (dev->info != &fountain_info) {
/* switch to raw sensor mode */
if (atp_geyser_init(dev))
return -EIO;
dev_info(&dev->intf->dev, "Geyser mode initialized.\n");
}
return 0;
}
static int atp_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct atp *dev;
struct input_dev *input_dev;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_endpoint_descriptor *ep;
int error;
const struct atp_info *info = (const struct atp_info *)id->driver_info;
/* set up the endpoint information */
/* use only the first interrupt-in endpoint */
error = usb_find_int_in_endpoint(iface->cur_altsetting, &ep);
if (error) {
dev_err(&iface->dev, "Could not find int-in endpoint\n");
return -EIO;
}
/* allocate memory for our device state and initialize it */
error = -ENOMEM;
dev = kzalloc_obj(*dev);
input_dev = input_allocate_device();
if (!dev || !input_dev) {
dev_err(&iface->dev, "Out of memory\n");
goto err_free_devs;
}
dev->udev = udev;
dev->intf = iface;
dev->input = input_dev;
dev->info = info;
dev->overflow_warned = false;
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb)
goto err_free_devs;
dev->data = usb_alloc_coherent(dev->udev, dev->info->datalen, GFP_KERNEL,
&dev->urb->transfer_dma);
if (!dev->data)
goto err_free_urb;
usb_fill_int_urb(dev->urb, udev,
usb_rcvintpipe(udev, usb_endpoint_num(ep)),
dev->data, dev->info->datalen,
dev->info->callback, dev, 1);
error = atp_handle_geyser(dev);
if (error)
goto err_free_buffer;
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
input_dev->name = "appletouch";
input_dev->phys = dev->phys;
usb_to_input_id(dev->udev, &input_dev->id);
input_dev->dev.parent = &iface->dev;
input_set_drvdata(input_dev, dev);
input_dev->open = atp_open;
input_dev->close = atp_close;
set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_X, 0,
(dev->info->xsensors - 1) * dev->info->xfact - 1,
dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
(dev->info->ysensors - 1) * dev->info->yfact - 1,
dev->info->fuzz, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(BTN_TOOL_FINGER, input_dev->keybit);
set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
set_bit(BTN_LEFT, input_dev->keybit);
INIT_WORK(&dev->work, atp_reinit);
error = input_register_device(dev->input);
if (error)
goto err_free_buffer;
/* save our data pointer in this interface device */
usb_set_intfdata(iface, dev);
return 0;
err_free_buffer:
usb_free_coherent(dev->udev, dev->info->datalen,
dev->data, dev->urb->transfer_dma);
err_free_urb:
usb_free_urb(dev->urb);
err_free_devs:
usb_set_intfdata(iface, NULL);
kfree(dev);
input_free_device(input_dev);
return error;
}
static void atp_disconnect(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
usb_set_intfdata(iface, NULL);
if (dev) {
usb_kill_urb(dev->urb);
input_unregister_device(dev->input);
usb_free_coherent(dev->udev, dev->info->datalen,
dev->data, dev->urb->transfer_dma);
usb_free_urb(dev->urb);
kfree(dev);
}
dev_info(&iface->dev, "input: appletouch disconnected\n");
}
static int atp_recover(struct atp *dev)
{
int error;
error = atp_handle_geyser(dev);
if (error)
return error;
guard(mutex)(&dev->input->mutex);
if (input_device_enabled(dev->input) && usb_submit_urb(dev->urb, GFP_KERNEL))
return -EIO;
return 0;
}
static int atp_suspend(struct usb_interface *iface, pm_message_t message)
{
struct atp *dev = usb_get_intfdata(iface);
usb_kill_urb(dev->urb);
return 0;
}
static int atp_resume(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
guard(mutex)(&dev->input->mutex);
if (input_device_enabled(dev->input) && usb_submit_urb(dev->urb, GFP_KERNEL))
return -EIO;
return 0;
}
static int atp_reset_resume(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
return atp_recover(dev);
}
static struct usb_driver atp_driver = {
.name = "appletouch",
.probe = atp_probe,
.disconnect = atp_disconnect,
.suspend = atp_suspend,
.resume = atp_resume,
.reset_resume = atp_reset_resume,
.id_table = atp_table,
};
module_usb_driver(atp_driver);
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Atari mouse driver for Linux/m68k
*
* Copyright (c) 2005 Michael Schmitz
*
* Based on:
* Amiga mouse driver for Linux/m68k
*
* Copyright (c) 2000-2002 Vojtech Pavlik
*/
/*
* The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
* (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
* interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
* This driver only deals with handing key events off to the input layer.
*
* Largely based on the old:
*
* Atari Mouse Driver for Linux
* by Robert de Vries (robert@and.nl) 19Jul93
*
* 16 Nov 1994 Andreas Schwab
* Compatibility with busmouse
* Support for three button mouse (shamelessly stolen from MiNT)
* third button wired to one of the joystick directions on joystick 1
*
* 1996/02/11 Andreas Schwab
* Module support
* Allow multiple open's
*
* Converted to use new generic busmouse code. 5 Apr 1998
* Russell King <rmk@arm.uk.linux.org>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <linux/uaccess.h>
#include <asm/atarihw.h>
#include <asm/atarikb.h>
#include <asm/atariints.h>
MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
MODULE_DESCRIPTION("Atari mouse driver");
MODULE_LICENSE("GPL");
static int mouse_threshold[2] = {2, 2};
module_param_array(mouse_threshold, int, NULL, 0);
#ifdef FIXED_ATARI_JOYSTICK
extern int atari_mouse_buttons;
#endif
static struct input_dev *atamouse_dev;
static void atamouse_interrupt(char *buf)
{
int buttons, dx, dy;
buttons = (buf[0] & 1) | ((buf[0] & 2) << 1);
#ifdef FIXED_ATARI_JOYSTICK
buttons |= atari_mouse_buttons & 2;
atari_mouse_buttons = buttons;
#endif
/* only relative events get here */
dx = buf[1];
dy = buf[2];
input_report_rel(atamouse_dev, REL_X, dx);
input_report_rel(atamouse_dev, REL_Y, dy);
input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x4);
input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2);
input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x1);
input_sync(atamouse_dev);
return;
}
static int atamouse_open(struct input_dev *dev)
{
#ifdef FIXED_ATARI_JOYSTICK
atari_mouse_buttons = 0;
#endif
ikbd_mouse_y0_top();
ikbd_mouse_thresh(mouse_threshold[0], mouse_threshold[1]);
ikbd_mouse_rel_pos();
atari_input_mouse_interrupt_hook = atamouse_interrupt;
return 0;
}
static void atamouse_close(struct input_dev *dev)
{
ikbd_mouse_disable();
atari_input_mouse_interrupt_hook = NULL;
}
static int __init atamouse_init(void)
{
int error;
if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
return -ENODEV;
error = atari_keyb_init();
if (error)
return error;
atamouse_dev = input_allocate_device();
if (!atamouse_dev)
return -ENOMEM;
atamouse_dev->name = "Atari mouse";
atamouse_dev->phys = "atamouse/input0";
atamouse_dev->id.bustype = BUS_HOST;
atamouse_dev->id.vendor = 0x0001;
atamouse_dev->id.product = 0x0002;
atamouse_dev->id.version = 0x0100;
atamouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
atamouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
atamouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
atamouse_dev->open = atamouse_open;
atamouse_dev->close = atamouse_close;
error = input_register_device(atamouse_dev);
if (error) {
input_free_device(atamouse_dev);
return error;
}
return 0;
}
static void __exit atamouse_exit(void)
{
input_unregister_device(atamouse_dev);
}
module_init(atamouse_init);
module_exit(atamouse_exit);
File diff suppressed because it is too large Load Diff
+507
View File
@@ -0,0 +1,507 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* BYD TouchPad PS/2 mouse driver
*
* Copyright (C) 2015 Chris Diamand <chris@diamand.org>
* Copyright (C) 2015 Richard Pospesel
* Copyright (C) 2015 Tai Chi Minh Ralph Eastwood
* Copyright (C) 2015 Martin Wimpress
* Copyright (C) 2015 Jay Kuri
*/
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/libps2.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "byd.h"
/* PS2 Bits */
#define PS2_Y_OVERFLOW BIT_MASK(7)
#define PS2_X_OVERFLOW BIT_MASK(6)
#define PS2_Y_SIGN BIT_MASK(5)
#define PS2_X_SIGN BIT_MASK(4)
#define PS2_ALWAYS_1 BIT_MASK(3)
#define PS2_MIDDLE BIT_MASK(2)
#define PS2_RIGHT BIT_MASK(1)
#define PS2_LEFT BIT_MASK(0)
/*
* BYD pad constants
*/
/*
* True device resolution is unknown, however experiments show the
* resolution is about 111 units/mm.
* Absolute coordinate packets are in the range 0-255 for both X and Y
* we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in
* the right ballpark given the touchpad's physical dimensions and estimate
* resolution per spec sheet, device active area dimensions are
* 101.6 x 60.1 mm.
*/
#define BYD_PAD_WIDTH 11264
#define BYD_PAD_HEIGHT 6656
#define BYD_PAD_RESOLUTION 111
/*
* Given the above dimensions, relative packets velocity is in multiples of
* 1 unit / 11 milliseconds. We use this dt to estimate distance traveled
*/
#define BYD_DT 11
/* Time in jiffies used to timeout various touch events (64 ms) */
#define BYD_TOUCH_TIMEOUT msecs_to_jiffies(64)
/* BYD commands reverse engineered from windows driver */
/*
* Swipe gesture from off-pad to on-pad
* 0 : disable
* 1 : enable
*/
#define BYD_CMD_SET_OFFSCREEN_SWIPE 0x10cc
/*
* Tap and drag delay time
* 0 : disable
* 1 - 8 : least to most delay
*/
#define BYD_CMD_SET_TAP_DRAG_DELAY_TIME 0x10cf
/*
* Physical buttons function mapping
* 0 : enable
* 4 : normal
* 5 : left button custom command
* 6 : right button custom command
* 8 : disable
*/
#define BYD_CMD_SET_PHYSICAL_BUTTONS 0x10d0
/*
* Absolute mode (1 byte X/Y resolution)
* 0 : disable
* 2 : enable
*/
#define BYD_CMD_SET_ABSOLUTE_MODE 0x10d1
/*
* Two finger scrolling
* 1 : vertical
* 2 : horizontal
* 3 : vertical + horizontal
* 4 : disable
*/
#define BYD_CMD_SET_TWO_FINGER_SCROLL 0x10d2
/*
* Handedness
* 1 : right handed
* 2 : left handed
*/
#define BYD_CMD_SET_HANDEDNESS 0x10d3
/*
* Tap to click
* 1 : enable
* 2 : disable
*/
#define BYD_CMD_SET_TAP 0x10d4
/*
* Tap and drag
* 1 : tap and hold to drag
* 2 : tap and hold to drag + lock
* 3 : disable
*/
#define BYD_CMD_SET_TAP_DRAG 0x10d5
/*
* Touch sensitivity
* 1 - 7 : least to most sensitive
*/
#define BYD_CMD_SET_TOUCH_SENSITIVITY 0x10d6
/*
* One finger scrolling
* 1 : vertical
* 2 : horizontal
* 3 : vertical + horizontal
* 4 : disable
*/
#define BYD_CMD_SET_ONE_FINGER_SCROLL 0x10d7
/*
* One finger scrolling function
* 1 : free scrolling
* 2 : edge motion
* 3 : free scrolling + edge motion
* 4 : disable
*/
#define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC 0x10d8
/*
* Sliding speed
* 1 - 5 : slowest to fastest
*/
#define BYD_CMD_SET_SLIDING_SPEED 0x10da
/*
* Edge motion
* 1 : disable
* 2 : enable when dragging
* 3 : enable when dragging and pointing
*/
#define BYD_CMD_SET_EDGE_MOTION 0x10db
/*
* Left edge region size
* 0 - 7 : smallest to largest width
*/
#define BYD_CMD_SET_LEFT_EDGE_REGION 0x10dc
/*
* Top edge region size
* 0 - 9 : smallest to largest height
*/
#define BYD_CMD_SET_TOP_EDGE_REGION 0x10dd
/*
* Disregard palm press as clicks
* 1 - 6 : smallest to largest
*/
#define BYD_CMD_SET_PALM_CHECK 0x10de
/*
* Right edge region size
* 0 - 7 : smallest to largest width
*/
#define BYD_CMD_SET_RIGHT_EDGE_REGION 0x10df
/*
* Bottom edge region size
* 0 - 9 : smallest to largest height
*/
#define BYD_CMD_SET_BOTTOM_EDGE_REGION 0x10e1
/*
* Multitouch gestures
* 1 : enable
* 2 : disable
*/
#define BYD_CMD_SET_MULTITOUCH 0x10e3
/*
* Edge motion speed
* 0 : control with finger pressure
* 1 - 9 : slowest to fastest
*/
#define BYD_CMD_SET_EDGE_MOTION_SPEED 0x10e4
/*
* Two finger scolling function
* 0 : free scrolling
* 1 : free scrolling (with momentum)
* 2 : edge motion
* 3 : free scrolling (with momentum) + edge motion
* 4 : disable
*/
#define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC 0x10e5
/*
* The touchpad generates a mixture of absolute and relative packets, indicated
* by the last byte of each packet being set to one of the following:
*/
#define BYD_PACKET_ABSOLUTE 0xf8
#define BYD_PACKET_RELATIVE 0x00
/* Multitouch gesture packets */
#define BYD_PACKET_PINCH_IN 0xd8
#define BYD_PACKET_PINCH_OUT 0x28
#define BYD_PACKET_ROTATE_CLOCKWISE 0x29
#define BYD_PACKET_ROTATE_ANTICLOCKWISE 0xd7
#define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT 0x2a
#define BYD_PACKET_TWO_FINGER_SCROLL_DOWN 0x2b
#define BYD_PACKET_TWO_FINGER_SCROLL_UP 0xd5
#define BYD_PACKET_TWO_FINGER_SCROLL_LEFT 0xd6
#define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT 0x2c
#define BYD_PACKET_THREE_FINGER_SWIPE_DOWN 0x2d
#define BYD_PACKET_THREE_FINGER_SWIPE_UP 0xd3
#define BYD_PACKET_THREE_FINGER_SWIPE_LEFT 0xd4
#define BYD_PACKET_FOUR_FINGER_DOWN 0x33
#define BYD_PACKET_FOUR_FINGER_UP 0xcd
#define BYD_PACKET_REGION_SCROLL_RIGHT 0x35
#define BYD_PACKET_REGION_SCROLL_DOWN 0x36
#define BYD_PACKET_REGION_SCROLL_UP 0xca
#define BYD_PACKET_REGION_SCROLL_LEFT 0xcb
#define BYD_PACKET_RIGHT_CORNER_CLICK 0xd2
#define BYD_PACKET_LEFT_CORNER_CLICK 0x2e
#define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK 0x2f
#define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT 0x37
#define BYD_PACKET_ONTO_PAD_SWIPE_DOWN 0x30
#define BYD_PACKET_ONTO_PAD_SWIPE_UP 0xd0
#define BYD_PACKET_ONTO_PAD_SWIPE_LEFT 0xc9
struct byd_data {
struct timer_list timer;
struct psmouse *psmouse;
s32 abs_x;
s32 abs_y;
typeof(jiffies) last_touch_time;
bool btn_left;
bool btn_right;
bool touch;
};
static void byd_report_input(struct psmouse *psmouse)
{
struct byd_data *priv = psmouse->private;
struct input_dev *dev = psmouse->dev;
input_report_key(dev, BTN_TOUCH, priv->touch);
input_report_key(dev, BTN_TOOL_FINGER, priv->touch);
input_report_abs(dev, ABS_X, priv->abs_x);
input_report_abs(dev, ABS_Y, priv->abs_y);
input_report_key(dev, BTN_LEFT, priv->btn_left);
input_report_key(dev, BTN_RIGHT, priv->btn_right);
input_sync(dev);
}
static void byd_clear_touch(struct timer_list *t)
{
struct byd_data *priv = timer_container_of(priv, t, timer);
struct psmouse *psmouse = priv->psmouse;
guard(serio_pause_rx)(psmouse->ps2dev.serio);
priv->touch = false;
byd_report_input(psmouse);
/*
* Move cursor back to center of pad when we lose touch - this
* specifically improves user experience when moving cursor with one
* finger, and pressing a button with another.
*/
priv->abs_x = BYD_PAD_WIDTH / 2;
priv->abs_y = BYD_PAD_HEIGHT / 2;
}
static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
{
struct byd_data *priv = psmouse->private;
u8 *pkt = psmouse->packet;
if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
pkt[0]);
return PSMOUSE_BAD_DATA;
}
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
/* Otherwise, a full packet has been received */
switch (pkt[3]) {
case BYD_PACKET_ABSOLUTE:
/* Only use absolute packets for the start of movement. */
if (!priv->touch) {
/* needed to detect tap */
typeof(jiffies) tap_time =
priv->last_touch_time + BYD_TOUCH_TIMEOUT;
priv->touch = time_after(jiffies, tap_time);
/* init abs position */
priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256);
priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256);
}
break;
case BYD_PACKET_RELATIVE: {
/* Standard packet */
/* Sign-extend if a sign bit is set. */
u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
s32 dx = signx | (int) pkt[1];
s32 dy = signy | (int) pkt[2];
/* Update position based on velocity */
priv->abs_x += dx * BYD_DT;
priv->abs_y -= dy * BYD_DT;
priv->touch = true;
break;
}
default:
psmouse_warn(psmouse, "Unrecognized Z: pkt = %*ph\n",
4, psmouse->packet);
return PSMOUSE_BAD_DATA;
}
priv->btn_left = pkt[0] & PS2_LEFT;
priv->btn_right = pkt[0] & PS2_RIGHT;
byd_report_input(psmouse);
/* Reset time since last touch. */
if (priv->touch) {
priv->last_touch_time = jiffies;
mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT);
}
return PSMOUSE_FULL_PACKET;
}
static int byd_reset_touchpad(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param[4];
size_t i;
static const struct {
u16 command;
u8 arg;
} seq[] = {
/*
* Intellimouse initialization sequence, to get 4-byte instead
* of 3-byte packets.
*/
{ PSMOUSE_CMD_SETRATE, 0xC8 },
{ PSMOUSE_CMD_SETRATE, 0x64 },
{ PSMOUSE_CMD_SETRATE, 0x50 },
{ PSMOUSE_CMD_GETID, 0 },
{ PSMOUSE_CMD_ENABLE, 0 },
/*
* BYD-specific initialization, which enables absolute mode and
* (if desired), the touchpad's built-in gesture detection.
*/
{ 0x10E2, 0x00 },
{ 0x10E0, 0x02 },
/* The touchpad should reply with 4 seemingly-random bytes */
{ 0x14E0, 0x01 },
/* Pairs of parameters and values. */
{ BYD_CMD_SET_HANDEDNESS, 0x01 },
{ BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 },
{ BYD_CMD_SET_TAP, 0x02 },
{ BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 },
{ BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 },
{ BYD_CMD_SET_EDGE_MOTION, 0x01 },
{ BYD_CMD_SET_PALM_CHECK, 0x00 },
{ BYD_CMD_SET_MULTITOUCH, 0x02 },
{ BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 },
{ BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 },
{ BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 },
{ BYD_CMD_SET_TOP_EDGE_REGION, 0x00 },
{ BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 },
{ BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 },
{ BYD_CMD_SET_ABSOLUTE_MODE, 0x02 },
/* Finalize initialization. */
{ 0x10E0, 0x00 },
{ 0x10E2, 0x01 },
};
for (i = 0; i < ARRAY_SIZE(seq); ++i) {
memset(param, 0, sizeof(param));
param[0] = seq[i].arg;
if (ps2_command(ps2dev, param, seq[i].command))
return -EIO;
}
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
return 0;
}
static int byd_reconnect(struct psmouse *psmouse)
{
int retry = 0, error = 0;
psmouse_dbg(psmouse, "Reconnect\n");
do {
psmouse_reset(psmouse);
if (retry)
ssleep(1);
error = byd_detect(psmouse, 0);
} while (error && ++retry < 3);
if (error)
return error;
psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
error = byd_reset_touchpad(psmouse);
if (error) {
psmouse_err(psmouse, "Unable to initialize device\n");
return error;
}
return 0;
}
static void byd_disconnect(struct psmouse *psmouse)
{
struct byd_data *priv = psmouse->private;
if (priv) {
timer_shutdown_sync(&priv->timer);
kfree(psmouse->private);
psmouse->private = NULL;
}
}
int byd_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param[4] = {0x03, 0x00, 0x00, 0x00};
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -1;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -1;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -1;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -1;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
if (param[1] != 0x03 || param[2] != 0x64)
return -ENODEV;
psmouse_dbg(psmouse, "BYD touchpad detected\n");
if (set_properties) {
psmouse->vendor = "BYD";
psmouse->name = "TouchPad";
}
return 0;
}
int byd_init(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct byd_data *priv;
if (psmouse_reset(psmouse))
return -EIO;
if (byd_reset_touchpad(psmouse))
return -EIO;
priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
priv->psmouse = psmouse;
timer_setup(&priv->timer, byd_clear_touch, 0);
psmouse->private = priv;
psmouse->disconnect = byd_disconnect;
psmouse->reconnect = byd_reconnect;
psmouse->protocol_handler = byd_process_byte;
psmouse->pktsize = 4;
psmouse->resync_time = 0;
__set_bit(INPUT_PROP_POINTER, dev->propbit);
/* Touchpad */
__set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_FINGER, dev->keybit);
/* Buttons */
__set_bit(BTN_LEFT, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit);
__clear_bit(BTN_MIDDLE, dev->keybit);
/* Absolute position */
__set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0);
input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION);
input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION);
/* No relative support */
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BYD_H
#define _BYD_H
int byd_detect(struct psmouse *psmouse, bool set_properties);
int byd_init(struct psmouse *psmouse);
#endif /* _BYD_H */
File diff suppressed because it is too large Load Diff
+446
View File
@@ -0,0 +1,446 @@
/*
* Cypress APA trackpad with I2C interface
*
* Author: Dudley Du <dudl@cypress.com>
*
* Copyright (C) 2014-2015 Cypress Semiconductor, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#ifndef _CYAPA_H
#define _CYAPA_H
#include <linux/firmware.h>
/* APA trackpad firmware generation number. */
#define CYAPA_GEN_UNKNOWN 0x00 /* unknown protocol. */
#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */
#define CYAPA_GEN5 0x05 /* support TrueTouch GEN5 trackpad device. */
#define CYAPA_GEN6 0x06 /* support TrueTouch GEN6 trackpad device. */
#define CYAPA_NAME "Cypress APA Trackpad (cyapa)"
/*
* Macros for SMBus communication
*/
#define SMBUS_READ 0x01
#define SMBUS_WRITE 0x00
#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
/* Commands for read/write registers of Cypress trackpad */
#define CYAPA_CMD_SOFT_RESET 0x00
#define CYAPA_CMD_POWER_MODE 0x01
#define CYAPA_CMD_DEV_STATUS 0x02
#define CYAPA_CMD_GROUP_DATA 0x03
#define CYAPA_CMD_GROUP_CMD 0x04
#define CYAPA_CMD_GROUP_QUERY 0x05
#define CYAPA_CMD_BL_STATUS 0x06
#define CYAPA_CMD_BL_HEAD 0x07
#define CYAPA_CMD_BL_CMD 0x08
#define CYAPA_CMD_BL_DATA 0x09
#define CYAPA_CMD_BL_ALL 0x0a
#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b
#define CYAPA_CMD_BLK_HEAD 0x0c
#define CYAPA_CMD_MAX_BASELINE 0x0d
#define CYAPA_CMD_MIN_BASELINE 0x0e
#define BL_HEAD_OFFSET 0x00
#define BL_DATA_OFFSET 0x10
#define BL_STATUS_SIZE 3 /* Length of gen3 bootloader status registers */
#define CYAPA_REG_MAP_SIZE 256
/*
* Gen3 Operational Device Status Register
*
* bit 7: Valid interrupt source
* bit 6 - 4: Reserved
* bit 3 - 2: Power status
* bit 1 - 0: Device status
*/
#define REG_OP_STATUS 0x00
#define OP_STATUS_SRC 0x80
#define OP_STATUS_POWER 0x0c
#define OP_STATUS_DEV 0x03
#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
/*
* Operational Finger Count/Button Flags Register
*
* bit 7 - 4: Number of touched finger
* bit 3: Valid data
* bit 2: Middle Physical Button
* bit 1: Right Physical Button
* bit 0: Left physical Button
*/
#define REG_OP_DATA1 0x01
#define OP_DATA_VALID 0x08
#define OP_DATA_MIDDLE_BTN 0x04
#define OP_DATA_RIGHT_BTN 0x02
#define OP_DATA_LEFT_BTN 0x01
#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
OP_DATA_LEFT_BTN)
/*
* Write-only command file register used to issue commands and
* parameters to the bootloader.
* The default value read from it is always 0x00.
*/
#define REG_BL_FILE 0x00
#define BL_FILE 0x00
/*
* Bootloader Status Register
*
* bit 7: Busy
* bit 6 - 5: Reserved
* bit 4: Bootloader running
* bit 3 - 2: Reserved
* bit 1: Watchdog Reset
* bit 0: Checksum valid
*/
#define REG_BL_STATUS 0x01
#define BL_STATUS_REV_6_5 0x60
#define BL_STATUS_BUSY 0x80
#define BL_STATUS_RUNNING 0x10
#define BL_STATUS_REV_3_2 0x0c
#define BL_STATUS_WATCHDOG 0x02
#define BL_STATUS_CSUM_VALID 0x01
#define BL_STATUS_REV_MASK (BL_STATUS_WATCHDOG | BL_STATUS_REV_3_2 | \
BL_STATUS_REV_6_5)
/*
* Bootloader Error Register
*
* bit 7: Invalid
* bit 6: Invalid security key
* bit 5: Bootloading
* bit 4: Command checksum
* bit 3: Flash protection error
* bit 2: Flash checksum error
* bit 1 - 0: Reserved
*/
#define REG_BL_ERROR 0x02
#define BL_ERROR_INVALID 0x80
#define BL_ERROR_INVALID_KEY 0x40
#define BL_ERROR_BOOTLOADING 0x20
#define BL_ERROR_CMD_CSUM 0x10
#define BL_ERROR_FLASH_PROT 0x08
#define BL_ERROR_FLASH_CSUM 0x04
#define BL_ERROR_RESERVED 0x03
#define BL_ERROR_NO_ERR_IDLE 0x00
#define BL_ERROR_NO_ERR_ACTIVE (BL_ERROR_BOOTLOADING)
#define CAPABILITY_BTN_SHIFT 3
#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3)
#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4)
#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5)
#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \
CAPABILITY_RIGHT_BTN_MASK | \
CAPABILITY_MIDDLE_BTN_MASK)
#define PWR_MODE_MASK 0xfc
#define PWR_MODE_FULL_ACTIVE (0x3f << 2)
#define PWR_MODE_IDLE (0x03 << 2) /* Default rt suspend scanrate: 30ms */
#define PWR_MODE_SLEEP (0x05 << 2) /* Default suspend scanrate: 50ms */
#define PWR_MODE_BTN_ONLY (0x01 << 2)
#define PWR_MODE_OFF (0x00 << 2)
#define PWR_STATUS_MASK 0x0c
#define PWR_STATUS_ACTIVE (0x03 << 2)
#define PWR_STATUS_IDLE (0x02 << 2)
#define PWR_STATUS_BTN_ONLY (0x01 << 2)
#define PWR_STATUS_OFF (0x00 << 2)
#define AUTOSUSPEND_DELAY 2000 /* unit : ms */
#define BTN_ONLY_MODE_NAME "buttononly"
#define OFF_MODE_NAME "off"
/* Common macros for PIP interface. */
#define PIP_HID_DESCRIPTOR_ADDR 0x0001
#define PIP_REPORT_DESCRIPTOR_ADDR 0x0002
#define PIP_INPUT_REPORT_ADDR 0x0003
#define PIP_OUTPUT_REPORT_ADDR 0x0004
#define PIP_CMD_DATA_ADDR 0x0006
#define PIP_RETRIEVE_DATA_STRUCTURE 0x24
#define PIP_CMD_CALIBRATE 0x28
#define PIP_BL_CMD_VERIFY_APP_INTEGRITY 0x31
#define PIP_BL_CMD_GET_BL_INFO 0x38
#define PIP_BL_CMD_PROGRAM_VERIFY_ROW 0x39
#define PIP_BL_CMD_LAUNCH_APP 0x3b
#define PIP_BL_CMD_INITIATE_BL 0x48
#define PIP_INVALID_CMD 0xff
#define PIP_HID_DESCRIPTOR_SIZE 32
#define PIP_HID_APP_REPORT_ID 0xf7
#define PIP_HID_BL_REPORT_ID 0xff
#define PIP_BL_CMD_REPORT_ID 0x40
#define PIP_BL_RESP_REPORT_ID 0x30
#define PIP_APP_CMD_REPORT_ID 0x2f
#define PIP_APP_RESP_REPORT_ID 0x1f
#define PIP_READ_SYS_INFO_CMD_LENGTH 7
#define PIP_BL_READ_APP_INFO_CMD_LENGTH 13
#define PIP_MIN_BL_CMD_LENGTH 13
#define PIP_MIN_BL_RESP_LENGTH 11
#define PIP_MIN_APP_CMD_LENGTH 7
#define PIP_MIN_APP_RESP_LENGTH 5
#define PIP_UNSUPPORTED_CMD_RESP_LENGTH 6
#define PIP_READ_SYS_INFO_RESP_LENGTH 71
#define PIP_BL_APP_INFO_RESP_LENGTH 30
#define PIP_BL_GET_INFO_RESP_LENGTH 19
#define PIP_BL_PLATFORM_VER_SHIFT 4
#define PIP_BL_PLATFORM_VER_MASK 0x0f
#define PIP_PRODUCT_FAMILY_MASK 0xf000
#define PIP_PRODUCT_FAMILY_TRACKPAD 0x1000
#define PIP_DEEP_SLEEP_STATE_ON 0x00
#define PIP_DEEP_SLEEP_STATE_OFF 0x01
#define PIP_DEEP_SLEEP_STATE_MASK 0x03
#define PIP_APP_DEEP_SLEEP_REPORT_ID 0xf0
#define PIP_DEEP_SLEEP_RESP_LENGTH 5
#define PIP_DEEP_SLEEP_OPCODE 0x08
#define PIP_DEEP_SLEEP_OPCODE_MASK 0x0f
#define PIP_RESP_LENGTH_OFFSET 0
#define PIP_RESP_LENGTH_SIZE 2
#define PIP_RESP_REPORT_ID_OFFSET 2
#define PIP_RESP_RSVD_OFFSET 3
#define PIP_RESP_RSVD_KEY 0x00
#define PIP_RESP_BL_SOP_OFFSET 4
#define PIP_SOP_KEY 0x01 /* Start of Packet */
#define PIP_EOP_KEY 0x17 /* End of Packet */
#define PIP_RESP_APP_CMD_OFFSET 4
#define GET_PIP_CMD_CODE(reg) ((reg) & 0x7f)
#define PIP_RESP_STATUS_OFFSET 5
#define VALID_CMD_RESP_HEADER(resp, cmd) \
(((resp)[PIP_RESP_REPORT_ID_OFFSET] == PIP_APP_RESP_REPORT_ID) && \
((resp)[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY) && \
(GET_PIP_CMD_CODE((resp)[PIP_RESP_APP_CMD_OFFSET]) == (cmd)))
#define PIP_CMD_COMPLETE_SUCCESS(resp_data) \
((resp_data)[PIP_RESP_STATUS_OFFSET] == 0x00)
/* Variables to record latest gen5 trackpad power states. */
#define UNINIT_SLEEP_TIME 0xffff
#define UNINIT_PWR_MODE 0xff
#define PIP_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s))
#define PIP_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode)
#define PIP_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t))
#define PIP_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time)
#define PIP_DEV_UNINIT_SLEEP_TIME(cyapa) \
(((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME)
/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
#define CYAPA_MAX_MT_SLOTS 15
struct cyapa;
typedef bool (*cb_sort)(struct cyapa *, u8 *, int);
enum cyapa_pm_stage {
CYAPA_PM_DEACTIVE,
CYAPA_PM_ACTIVE,
CYAPA_PM_SUSPEND,
CYAPA_PM_RESUME,
CYAPA_PM_RUNTIME_SUSPEND,
CYAPA_PM_RUNTIME_RESUME,
};
struct cyapa_dev_ops {
int (*check_fw)(struct cyapa *, const struct firmware *);
int (*bl_enter)(struct cyapa *);
int (*bl_activate)(struct cyapa *);
int (*bl_initiate)(struct cyapa *, const struct firmware *);
int (*update_fw)(struct cyapa *, const struct firmware *);
int (*bl_deactivate)(struct cyapa *);
ssize_t (*show_baseline)(struct device *,
struct device_attribute *, char *);
ssize_t (*calibrate_store)(struct device *,
struct device_attribute *, const char *, size_t);
int (*initialize)(struct cyapa *cyapa);
int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len);
int (*operational_check)(struct cyapa *cyapa);
int (*irq_handler)(struct cyapa *);
bool (*irq_cmd_handler)(struct cyapa *);
int (*sort_empty_output_data)(struct cyapa *,
u8 *, int *, cb_sort);
int (*set_power_mode)(struct cyapa *, u8, u16, enum cyapa_pm_stage);
int (*set_proximity)(struct cyapa *, bool);
};
struct cyapa_pip_cmd_states {
struct mutex cmd_lock;
struct completion cmd_ready;
atomic_t cmd_issued;
u8 in_progress_cmd;
bool is_irq_mode;
cb_sort resp_sort_func;
u8 *resp_data;
int *resp_len;
enum cyapa_pm_stage pm_stage;
struct mutex pm_stage_lock;
u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE];
u8 empty_buf[CYAPA_REG_MAP_SIZE];
};
union cyapa_cmd_states {
struct cyapa_pip_cmd_states pip;
};
enum cyapa_state {
CYAPA_STATE_NO_DEVICE,
CYAPA_STATE_BL_BUSY,
CYAPA_STATE_BL_IDLE,
CYAPA_STATE_BL_ACTIVE,
CYAPA_STATE_OP,
CYAPA_STATE_GEN5_BL,
CYAPA_STATE_GEN5_APP,
CYAPA_STATE_GEN6_BL,
CYAPA_STATE_GEN6_APP,
};
struct gen6_interval_setting {
u16 active_interval;
u16 lp1_interval;
u16 lp2_interval;
};
/* The main device structure */
struct cyapa {
enum cyapa_state state;
u8 status[BL_STATUS_SIZE];
bool operational; /* true: ready for data reporting; false: not. */
struct regulator *vcc;
struct i2c_client *client;
struct input_dev *input;
char phys[32]; /* Device physical location */
bool irq_wake; /* Irq wake is enabled */
bool smbus;
/* power mode settings */
u8 suspend_power_mode;
u16 suspend_sleep_time;
u8 runtime_suspend_power_mode;
u16 runtime_suspend_sleep_time;
u8 dev_pwr_mode;
u16 dev_sleep_time;
struct gen6_interval_setting gen6_interval_setting;
/* Read from query data region. */
char product_id[16];
u8 platform_ver; /* Platform version. */
u8 fw_maj_ver; /* Firmware major version. */
u8 fw_min_ver; /* Firmware minor version. */
u8 btn_capability;
u8 gen;
int max_abs_x;
int max_abs_y;
int physical_size_x;
int physical_size_y;
/* Used in ttsp and truetouch based trackpad devices. */
u8 x_origin; /* X Axis Origin: 0 = left side; 1 = right side. */
u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */
int electrodes_x; /* Number of electrodes on the X Axis*/
int electrodes_y; /* Number of electrodes on the Y Axis*/
int electrodes_rx; /* Number of Rx electrodes */
int aligned_electrodes_rx; /* 4 aligned */
int max_z;
/*
* Used to synchronize the access or update the device state.
* And since update firmware and read firmware image process will take
* quite long time, maybe more than 10 seconds, so use mutex_lock
* to sync and wait other interface and detecting are done or ready.
*/
struct mutex state_sync_lock;
const struct cyapa_dev_ops *ops;
union cyapa_cmd_states cmd_states;
};
ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
u8 *values);
ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
u8 *values);
ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values);
int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout);
u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time);
u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size);
ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size);
int cyapa_empty_pip_output_data(struct cyapa *cyapa,
u8 *buf, int *len, cb_sort func);
int cyapa_i2c_pip_cmd_irq_sync(struct cyapa *cyapa,
u8 *cmd, int cmd_len,
u8 *resp_data, int *resp_len,
unsigned long timeout,
cb_sort func,
bool irq_mode);
int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len);
bool cyapa_pip_sort_system_info_data(struct cyapa *cyapa, u8 *buf, int len);
bool cyapa_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, u8 *data, int len);
int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state);
bool cyapa_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, u8 *data, int len);
int cyapa_pip_bl_exit(struct cyapa *cyapa);
int cyapa_pip_bl_enter(struct cyapa *cyapa);
bool cyapa_is_pip_bl_mode(struct cyapa *cyapa);
bool cyapa_is_pip_app_mode(struct cyapa *cyapa);
int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa);
int cyapa_pip_resume_scanning(struct cyapa *cyapa);
int cyapa_pip_suspend_scanning(struct cyapa *cyapa);
int cyapa_pip_check_fw(struct cyapa *cyapa, const struct firmware *fw);
int cyapa_pip_bl_initiate(struct cyapa *cyapa, const struct firmware *fw);
int cyapa_pip_do_fw_update(struct cyapa *cyapa, const struct firmware *fw);
int cyapa_pip_bl_activate(struct cyapa *cyapa);
int cyapa_pip_bl_deactivate(struct cyapa *cyapa);
ssize_t cyapa_pip_do_calibrate(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable);
bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa);
int cyapa_pip_irq_handler(struct cyapa *cyapa);
extern u8 pip_read_sys_info[];
extern u8 pip_bl_read_app_info[];
extern const char product_id[];
extern const struct cyapa_dev_ops cyapa_gen3_ops;
extern const struct cyapa_dev_ops cyapa_gen5_ops;
extern const struct cyapa_dev_ops cyapa_gen6_ops;
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,746 @@
/*
* Cypress APA trackpad with I2C interface
*
* Author: Dudley Du <dudl@cypress.com>
*
* Copyright (C) 2015 Cypress Semiconductor, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/unaligned.h>
#include <linux/crc-itu-t.h>
#include "cyapa.h"
#define GEN6_ENABLE_CMD_IRQ 0x41
#define GEN6_DISABLE_CMD_IRQ 0x42
#define GEN6_ENABLE_DEV_IRQ 0x43
#define GEN6_DISABLE_DEV_IRQ 0x44
#define GEN6_POWER_MODE_ACTIVE 0x01
#define GEN6_POWER_MODE_LP_MODE1 0x02
#define GEN6_POWER_MODE_LP_MODE2 0x03
#define GEN6_POWER_MODE_BTN_ONLY 0x04
#define GEN6_SET_POWER_MODE_INTERVAL 0x47
#define GEN6_GET_POWER_MODE_INTERVAL 0x48
#define GEN6_MAX_RX_NUM 14
#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00
#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12
struct pip_app_cmd_head {
__le16 addr;
__le16 length;
u8 report_id;
u8 resv; /* Reserved, must be 0 */
u8 cmd_code; /* bit7: resv, set to 0; bit6~0: command code.*/
} __packed;
struct pip_app_resp_head {
__le16 length;
u8 report_id;
u8 resv; /* Reserved, must be 0 */
u8 cmd_code; /* bit7: TGL; bit6~0: command code.*/
/*
* The value of data_status can be the first byte of data or
* the command status or the unsupported command code depending on the
* requested command code.
*/
u8 data_status;
} __packed;
struct pip_fixed_info {
u8 silicon_id_high;
u8 silicon_id_low;
u8 family_id;
};
static u8 pip_get_bl_info[] = {
0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
0x00, 0x00, 0x70, 0x9E, 0x17
};
static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
u8 *buf, int len)
{
if (len != PIP_HID_DESCRIPTOR_SIZE)
return false;
if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
return true;
return false;
}
static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
struct pip_fixed_info *pip_info, bool is_bootloader)
{
u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
int resp_len;
u16 product_family;
int error;
if (is_bootloader) {
/* Read Bootloader Information to determine Gen5 or Gen6. */
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
pip_get_bl_info, sizeof(pip_get_bl_info),
resp_data, &resp_len,
2000, cyapa_sort_tsg_pip_bl_resp_data,
false);
if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
return error ? error : -EIO;
pip_info->family_id = resp_data[8];
pip_info->silicon_id_low = resp_data[10];
pip_info->silicon_id_high = resp_data[11];
return 0;
}
/* Get App System Information to determine Gen5 or Gen6. */
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
resp_data, &resp_len,
2000, cyapa_pip_sort_system_info_data, false);
if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
return error ? error : -EIO;
product_family = get_unaligned_le16(&resp_data[7]);
if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
PIP_PRODUCT_FAMILY_TRACKPAD)
return -EINVAL;
pip_info->family_id = resp_data[19];
pip_info->silicon_id_low = resp_data[21];
pip_info->silicon_id_high = resp_data[22];
return 0;
}
int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
{
u8 cmd[] = { 0x01, 0x00};
struct pip_fixed_info pip_info;
u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
int resp_len;
bool is_bootloader;
int error;
cyapa->state = CYAPA_STATE_NO_DEVICE;
/* Try to wake from it deep sleep state if it is. */
cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
/* Empty the buffer queue to get fresh data with later commands. */
cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
/*
* Read description info from trackpad device to determine running in
* APP mode or Bootloader mode.
*/
resp_len = PIP_HID_DESCRIPTOR_SIZE;
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
cmd, sizeof(cmd),
resp_data, &resp_len,
300,
cyapa_sort_pip_hid_descriptor_data,
false);
if (error)
return error;
if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
is_bootloader = true;
else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
is_bootloader = false;
else
return -EAGAIN;
/* Get PIP fixed information to determine Gen5 or Gen6. */
memset(&pip_info, 0, sizeof(struct pip_fixed_info));
error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
if (error)
return error;
if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
cyapa->gen = CYAPA_GEN6;
cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
: CYAPA_STATE_GEN6_APP;
} else if (pip_info.family_id == 0x91 &&
pip_info.silicon_id_high == 0x02) {
cyapa->gen = CYAPA_GEN5;
cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
: CYAPA_STATE_GEN5_APP;
}
return 0;
}
static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
{
u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
int resp_len;
u16 product_family;
u8 rotat_align;
int error;
/* Get App System Information to determine Gen5 or Gen6. */
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
resp_data, &resp_len,
2000, cyapa_pip_sort_system_info_data, false);
if (error || resp_len < sizeof(resp_data))
return error ? error : -EIO;
product_family = get_unaligned_le16(&resp_data[7]);
if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
PIP_PRODUCT_FAMILY_TRACKPAD)
return -EINVAL;
cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
PIP_BL_PLATFORM_VER_MASK;
cyapa->fw_maj_ver = resp_data[9];
cyapa->fw_min_ver = resp_data[10];
cyapa->electrodes_x = resp_data[33];
cyapa->electrodes_y = resp_data[34];
cyapa->physical_size_x = get_unaligned_le16(&resp_data[35]) / 100;
cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
cyapa->max_z = get_unaligned_le16(&resp_data[43]);
cyapa->x_origin = resp_data[45] & 0x01;
cyapa->y_origin = resp_data[46] & 0x01;
cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
memcpy(&cyapa->product_id[0], &resp_data[51], 5);
cyapa->product_id[5] = '-';
memcpy(&cyapa->product_id[6], &resp_data[56], 6);
cyapa->product_id[12] = '-';
memcpy(&cyapa->product_id[13], &resp_data[62], 2);
cyapa->product_id[15] = '\0';
/* Get the number of Rx electrodes. */
rotat_align = resp_data[68];
cyapa->electrodes_rx =
rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x;
cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
!cyapa->physical_size_x || !cyapa->physical_size_y ||
!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
return -EINVAL;
return 0;
}
static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
{
u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
int resp_len;
int error;
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
resp_data, &resp_len,
500, cyapa_sort_tsg_pip_bl_resp_data, false);
if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
!PIP_CMD_COMPLETE_SUCCESS(resp_data))
return error ? error : -EIO;
cyapa->fw_maj_ver = resp_data[8];
cyapa->fw_min_ver = resp_data[9];
cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
PIP_BL_PLATFORM_VER_MASK;
memcpy(&cyapa->product_id[0], &resp_data[13], 5);
cyapa->product_id[5] = '-';
memcpy(&cyapa->product_id[6], &resp_data[18], 6);
cyapa->product_id[12] = '-';
memcpy(&cyapa->product_id[13], &resp_data[24], 2);
cyapa->product_id[15] = '\0';
return 0;
}
static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
{
u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
u8 resp_data[6];
int resp_len;
int error;
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
resp_data, &resp_len,
500, cyapa_sort_tsg_pip_app_resp_data, false);
if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
!PIP_CMD_COMPLETE_SUCCESS(resp_data)
)
return error < 0 ? error : -EINVAL;
return 0;
}
static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
{
int error;
cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
error = cyapa_pip_set_proximity(cyapa, enable);
cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
return error;
}
static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
{
u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
u8 resp_data[6];
int resp_len;
int error;
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
resp_data, &resp_len,
500, cyapa_sort_tsg_pip_app_resp_data, false);
if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
return error < 0 ? error : -EINVAL;
/* New power state applied in device not match the set power state. */
if (resp_data[5] != power_mode)
return -EAGAIN;
return 0;
}
static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
struct gen6_interval_setting *interval_setting)
{
struct gen6_set_interval_cmd {
__le16 addr;
__le16 length;
u8 report_id;
u8 rsvd; /* Reserved, must be 0 */
u8 cmd_code;
__le16 active_interval;
__le16 lp1_interval;
__le16 lp2_interval;
} __packed set_interval_cmd;
u8 resp_data[11];
int resp_len;
int error;
memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
put_unaligned_le16(sizeof(set_interval_cmd) - 2,
&set_interval_cmd.length);
set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
put_unaligned_le16(interval_setting->active_interval,
&set_interval_cmd.active_interval);
put_unaligned_le16(interval_setting->lp1_interval,
&set_interval_cmd.lp1_interval);
put_unaligned_le16(interval_setting->lp2_interval,
&set_interval_cmd.lp2_interval);
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
(u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
resp_data, &resp_len,
500, cyapa_sort_tsg_pip_app_resp_data, false);
if (error ||
!VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
return error < 0 ? error : -EINVAL;
/* Get the real set intervals from response. */
interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
return 0;
}
static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
struct gen6_interval_setting *interval_setting)
{
u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
GEN6_GET_POWER_MODE_INTERVAL };
u8 resp_data[11];
int resp_len;
int error;
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
resp_data, &resp_len,
500, cyapa_sort_tsg_pip_app_resp_data, false);
if (error ||
!VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
return error < 0 ? error : -EINVAL;
interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
return 0;
}
static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
{
u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
if (state == PIP_DEEP_SLEEP_STATE_ON)
/*
* Send ping command to notify device prepare for wake up
* when it's in deep sleep mode. At this time, device will
* response nothing except an I2C NAK.
*/
cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
return cyapa_pip_deep_sleep(cyapa, state);
}
static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
{
struct device *dev = &cyapa->client->dev;
struct gen6_interval_setting *interval_setting =
&cyapa->gen6_interval_setting;
u8 lp_mode;
int error;
if (cyapa->state != CYAPA_STATE_GEN6_APP)
return 0;
if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
/*
* Assume TP in deep sleep mode when driver is loaded,
* avoid driver unload and reload command IO issue caused by TP
* has been set into deep sleep mode when unloading.
*/
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
}
if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
if (power_mode == PWR_MODE_OFF ||
power_mode == PWR_MODE_FULL_ACTIVE ||
power_mode == PWR_MODE_BTN_ONLY ||
PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
/* Has in correct power mode state, early return. */
return 0;
}
}
if (power_mode == PWR_MODE_OFF) {
cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
if (error) {
dev_err(dev, "enter deep sleep fail: %d\n", error);
return error;
}
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
return 0;
}
/*
* When trackpad in power off mode, it cannot change to other power
* state directly, must be wake up from sleep firstly, then
* continue to do next power sate change.
*/
if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
if (error) {
dev_err(dev, "deep sleep wake fail: %d\n", error);
return error;
}
}
/*
* Disable device assert interrupts for command response to avoid
* disturbing system suspending or hibernating process.
*/
cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
if (power_mode == PWR_MODE_FULL_ACTIVE) {
error = cyapa_gen6_change_power_state(cyapa,
GEN6_POWER_MODE_ACTIVE);
if (error) {
dev_err(dev, "change to active fail: %d\n", error);
goto out;
}
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
/* Sync the interval setting from device. */
cyapa_gen6_get_interval_setting(cyapa, interval_setting);
} else if (power_mode == PWR_MODE_BTN_ONLY) {
error = cyapa_gen6_change_power_state(cyapa,
GEN6_POWER_MODE_BTN_ONLY);
if (error) {
dev_err(dev, "fail to button only mode: %d\n", error);
goto out;
}
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
} else {
/*
* Gen6 internally supports to 2 low power scan interval time,
* so can help to switch power mode quickly.
* such as runtime suspend and system suspend.
*/
if (interval_setting->lp1_interval == sleep_time) {
lp_mode = GEN6_POWER_MODE_LP_MODE1;
} else if (interval_setting->lp2_interval == sleep_time) {
lp_mode = GEN6_POWER_MODE_LP_MODE2;
} else {
if (interval_setting->lp1_interval == 0) {
interval_setting->lp1_interval = sleep_time;
lp_mode = GEN6_POWER_MODE_LP_MODE1;
} else {
interval_setting->lp2_interval = sleep_time;
lp_mode = GEN6_POWER_MODE_LP_MODE2;
}
cyapa_gen6_set_interval_setting(cyapa,
interval_setting);
}
error = cyapa_gen6_change_power_state(cyapa, lp_mode);
if (error) {
dev_err(dev, "set power state to 0x%02x failed: %d\n",
lp_mode, error);
goto out;
}
PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
PIP_DEV_SET_PWR_STATE(cyapa,
cyapa_sleep_time_to_pwr_cmd(sleep_time));
}
out:
cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
return error;
}
static int cyapa_gen6_initialize(struct cyapa *cyapa)
{
return 0;
}
static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
u16 read_offset, u16 read_len, u8 data_id,
u8 *data, int *data_buf_lens)
{
struct retrieve_data_struct_cmd {
struct pip_app_cmd_head head;
__le16 read_offset;
__le16 read_length;
u8 data_id;
} __packed cmd;
u8 resp_data[GEN6_MAX_RX_NUM + 10];
int resp_len;
int error;
memset(&cmd, 0, sizeof(cmd));
put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
put_unaligned_le16(sizeof(cmd) - 2, &cmd.head.length);
cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
put_unaligned_le16(read_offset, &cmd.read_offset);
put_unaligned_le16(read_len, &cmd.read_length);
cmd.data_id = data_id;
resp_len = sizeof(resp_data);
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
(u8 *)&cmd, sizeof(cmd),
resp_data, &resp_len,
500, cyapa_sort_tsg_pip_app_resp_data,
true);
if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
resp_data[6] != data_id ||
!VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
return (error < 0) ? error : -EAGAIN;
read_len = get_unaligned_le16(&resp_data[7]);
if (*data_buf_lens < read_len) {
*data_buf_lens = read_len;
return -ENOBUFS;
}
memcpy(data, &resp_data[10], read_len);
*data_buf_lens = read_len;
return 0;
}
static ssize_t cyapa_gen6_show_baseline(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cyapa *cyapa = dev_get_drvdata(dev);
u8 data[GEN6_MAX_RX_NUM];
int data_len;
int size = 0;
int i;
int error;
int resume_error;
if (!cyapa_is_pip_app_mode(cyapa))
return -EBUSY;
/* 1. Suspend Scanning*/
error = cyapa_pip_suspend_scanning(cyapa);
if (error)
return error;
/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
data_len = sizeof(data);
error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
data, &data_len);
if (error)
goto resume_scanning;
size = sysfs_emit(buf, "%d %d %d %d %d %d ",
data[0], /* RX Attenuator Mutual */
data[1], /* IDAC Mutual */
data[2], /* RX Attenuator Self RX */
data[3], /* IDAC Self RX */
data[4], /* RX Attenuator Self TX */
data[5] /* IDAC Self TX */
);
/* 3. Read Attenuator Trim. */
data_len = sizeof(data);
error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
data, &data_len);
if (error)
goto resume_scanning;
/* set attenuator trim values. */
for (i = 0; i < data_len; i++)
size += sysfs_emit_at(buf, size, "%d ", data[i]);
size += sysfs_emit_at(buf, size, "\n");
resume_scanning:
/* 4. Resume Scanning*/
resume_error = cyapa_pip_resume_scanning(cyapa);
if (resume_error || error) {
memset(buf, 0, PAGE_SIZE);
return resume_error ? resume_error : error;
}
return size;
}
static int cyapa_gen6_operational_check(struct cyapa *cyapa)
{
struct device *dev = &cyapa->client->dev;
int error;
if (cyapa->gen != CYAPA_GEN6)
return -ENODEV;
switch (cyapa->state) {
case CYAPA_STATE_GEN6_BL:
error = cyapa_pip_bl_exit(cyapa);
if (error) {
/* Try to update trackpad product information. */
cyapa_gen6_bl_read_app_info(cyapa);
goto out;
}
cyapa->state = CYAPA_STATE_GEN6_APP;
fallthrough;
case CYAPA_STATE_GEN6_APP:
/*
* If trackpad device in deep sleep mode,
* the app command will fail.
* So always try to reset trackpad device to full active when
* the device state is required.
*/
error = cyapa_gen6_set_power_mode(cyapa,
PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
if (error)
dev_warn(dev, "%s: failed to set power active mode.\n",
__func__);
/* By default, the trackpad proximity function is enabled. */
error = cyapa_pip_set_proximity(cyapa, true);
if (error)
dev_warn(dev, "%s: failed to enable proximity.\n",
__func__);
/* Get trackpad product information. */
error = cyapa_gen6_read_sys_info(cyapa);
if (error)
goto out;
/* Only support product ID starting with CYTRA */
if (memcmp(cyapa->product_id, product_id,
strlen(product_id)) != 0) {
dev_err(dev, "%s: unknown product ID (%s)\n",
__func__, cyapa->product_id);
error = -EINVAL;
}
break;
default:
error = -EINVAL;
}
out:
return error;
}
const struct cyapa_dev_ops cyapa_gen6_ops = {
.check_fw = cyapa_pip_check_fw,
.bl_enter = cyapa_pip_bl_enter,
.bl_initiate = cyapa_pip_bl_initiate,
.update_fw = cyapa_pip_do_fw_update,
.bl_activate = cyapa_pip_bl_activate,
.bl_deactivate = cyapa_pip_bl_deactivate,
.show_baseline = cyapa_gen6_show_baseline,
.calibrate_store = cyapa_pip_do_calibrate,
.initialize = cyapa_gen6_initialize,
.state_parse = cyapa_pip_state_parse,
.operational_check = cyapa_gen6_operational_check,
.irq_handler = cyapa_pip_irq_handler,
.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
.sort_empty_output_data = cyapa_empty_pip_output_data,
.set_power_mode = cyapa_gen6_set_power_mode,
.set_proximity = cyapa_gen6_set_proximity,
};
@@ -0,0 +1,675 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Cypress Trackpad PS/2 mouse driver
*
* Copyright (c) 2012 Cypress Semiconductor Corporation.
*
* Author:
* Dudley Du <dudl@cypress.com>
*
* Additional contributors include:
* Kamal Mostafa <kamal@canonical.com>
* Kyle Fazzari <git@status.e4ward.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include "cypress_ps2.h"
#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */
static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
{
struct cytp_data *cytp = psmouse->private;
cytp->pkt_size = n;
}
static const u8 cytp_rate[] = {10, 20, 40, 60, 100, 200};
static const u8 cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
static int cypress_ps2_sendbyte(struct psmouse *psmouse, u8 cmd)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
int error;
error = ps2_sendbyte(ps2dev, cmd, CYTP_CMD_TIMEOUT);
if (error) {
psmouse_dbg(psmouse,
"sending command 0x%02x failed, resp 0x%02x, error %d\n",
cmd, ps2dev->nak, error);
return error;
}
#ifdef CYTP_DEBUG_VERBOSE
psmouse_dbg(psmouse, "sending command 0x%02x succeeded\n", cmd);
#endif
return 0;
}
static int cypress_ps2_ext_cmd(struct psmouse *psmouse, u8 prefix, u8 nibble)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
int tries = CYTP_PS2_CMD_TRIES;
int rc;
ps2_begin_command(ps2dev);
do {
/*
* Send extension command byte (0xE8 or 0xF3).
* If sending the command fails, send recovery command
* to make the device return to the ready state.
*/
rc = cypress_ps2_sendbyte(psmouse, prefix);
if (rc == -EAGAIN) {
rc = cypress_ps2_sendbyte(psmouse, 0x00);
if (rc == -EAGAIN)
rc = cypress_ps2_sendbyte(psmouse, 0x0a);
}
if (!rc) {
rc = cypress_ps2_sendbyte(psmouse, nibble);
if (rc == -EAGAIN)
rc = cypress_ps2_sendbyte(psmouse, nibble);
if (!rc)
break;
}
} while (--tries > 0);
ps2_end_command(ps2dev);
return rc;
}
static bool cypress_verify_cmd_state(struct psmouse *psmouse, u8 cmd, u8* param)
{
bool rate_match = false;
bool resolution_match = false;
int i;
/* callers will do further checking. */
if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
cmd == CYTP_CMD_STANDARD_MODE ||
cmd == CYTP_CMD_READ_TP_METRICS)
return true;
if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
(param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
for (i = 0; i < sizeof(cytp_resolution); i++)
if (cytp_resolution[i] == param[1])
resolution_match = true;
for (i = 0; i < sizeof(cytp_rate); i++)
if (cytp_rate[i] == param[2])
rate_match = true;
if (resolution_match && rate_match)
return true;
}
psmouse_dbg(psmouse, "verify cmd state failed.\n");
return false;
}
static int cypress_send_ext_cmd(struct psmouse *psmouse, u8 cmd, u8 *param)
{
u8 cmd_prefix = PSMOUSE_CMD_SETRES & 0xff;
unsigned int resp_size = cmd == CYTP_CMD_READ_TP_METRICS ? 8 : 3;
unsigned int ps2_cmd = (PSMOUSE_CMD_GETINFO & 0xff) | (resp_size << 8);
int tries = CYTP_PS2_CMD_TRIES;
int error;
psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
do {
cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_DD(cmd));
cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_CC(cmd));
cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_BB(cmd));
cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_AA(cmd));
error = ps2_command(&psmouse->ps2dev, param, ps2_cmd);
if (error) {
psmouse_dbg(psmouse, "Command 0x%02x failed: %d\n",
cmd, error);
} else {
psmouse_dbg(psmouse,
"Command 0x%02x response data (0x): %*ph\n",
cmd, resp_size, param);
if (cypress_verify_cmd_state(psmouse, cmd, param))
return 0;
}
} while (--tries > 0);
return -EIO;
}
int cypress_detect(struct psmouse *psmouse, bool set_properties)
{
u8 param[3];
if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
return -ENODEV;
/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
if (param[0] != 0x33 || param[1] != 0xCC)
return -ENODEV;
if (set_properties) {
psmouse->vendor = "Cypress";
psmouse->name = "Trackpad";
}
return 0;
}
static int cypress_read_fw_version(struct psmouse *psmouse)
{
struct cytp_data *cytp = psmouse->private;
u8 param[3];
if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
return -ENODEV;
/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
if (param[0] != 0x33 || param[1] != 0xCC)
return -ENODEV;
cytp->fw_version = param[2] & FW_VERSION_MASX;
cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
/*
* Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to
* CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594.
*/
if (cytp->fw_version >= 11)
cytp->tp_metrics_supported = 0;
psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
cytp->tp_metrics_supported);
return 0;
}
static int cypress_read_tp_metrics(struct psmouse *psmouse)
{
struct cytp_data *cytp = psmouse->private;
u8 param[8];
/* set default values for tp metrics. */
cytp->tp_width = CYTP_DEFAULT_WIDTH;
cytp->tp_high = CYTP_DEFAULT_HIGH;
cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
if (!cytp->tp_metrics_supported)
return 0;
memset(param, 0, sizeof(param));
if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
/* Update trackpad parameters. */
cytp->tp_max_abs_x = (param[1] << 8) | param[0];
cytp->tp_max_abs_y = (param[3] << 8) | param[2];
cytp->tp_min_pressure = param[4];
cytp->tp_max_pressure = param[5];
}
if (!cytp->tp_max_pressure ||
cytp->tp_max_pressure < cytp->tp_min_pressure ||
!cytp->tp_width || !cytp->tp_high ||
!cytp->tp_max_abs_x ||
cytp->tp_max_abs_x < cytp->tp_width ||
!cytp->tp_max_abs_y ||
cytp->tp_max_abs_y < cytp->tp_high)
return -EINVAL;
cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
#ifdef CYTP_DEBUG_VERBOSE
psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
psmouse_dbg(psmouse, "tp_type_APA = %d\n",
(param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
(param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
psmouse_dbg(psmouse, "tp_palm = %d\n",
(param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
psmouse_dbg(psmouse, "tp_stubborn = %d\n",
(param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
(param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
(param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
param[7] & TP_METRICS_BIT_1F_SPIKE);
psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
(param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
(param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
#endif
return 0;
}
static int cypress_query_hardware(struct psmouse *psmouse)
{
int error;
error = cypress_read_fw_version(psmouse);
if (error)
return error;
error = cypress_read_tp_metrics(psmouse);
if (error)
return error;
return 0;
}
static int cypress_set_absolute_mode(struct psmouse *psmouse)
{
struct cytp_data *cytp = psmouse->private;
u8 param[3];
int error;
error = cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE,
param);
if (error)
return error;
cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
| CYTP_BIT_ABS_PRESSURE;
cypress_set_packet_size(psmouse, 5);
return 0;
}
/*
* Reset trackpad device.
* This is also the default mode when trackpad powered on.
*/
static void cypress_reset(struct psmouse *psmouse)
{
struct cytp_data *cytp = psmouse->private;
cytp->mode = 0;
psmouse_reset(psmouse);
}
static int cypress_set_input_params(struct input_dev *input,
struct cytp_data *cytp)
{
int error;
if (!cytp->tp_res_x || !cytp->tp_res_y)
return -EINVAL;
__set_bit(EV_ABS, input->evbit);
input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
input_set_abs_params(input, ABS_PRESSURE,
cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
/* finger position */
input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
error = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
if (error)
return error;
__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
input_abs_set_res(input, ABS_X, cytp->tp_res_x);
input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
__set_bit(BTN_TOUCH, input->keybit);
__set_bit(BTN_TOOL_FINGER, input->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
__set_bit(BTN_TOOL_QUADTAP, input->keybit);
__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
__clear_bit(EV_REL, input->evbit);
__clear_bit(REL_X, input->relbit);
__clear_bit(REL_Y, input->relbit);
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_LEFT, input->keybit);
__set_bit(BTN_RIGHT, input->keybit);
__set_bit(BTN_MIDDLE, input->keybit);
return 0;
}
static int cypress_get_finger_count(u8 header_byte)
{
u8 bits6_7;
int finger_count;
bits6_7 = header_byte >> 6;
finger_count = bits6_7 & 0x03;
if (finger_count == 1)
return 1;
if (header_byte & ABS_HSCROLL_BIT) {
/* HSCROLL gets added on to 0 finger count. */
switch (finger_count) {
case 0: return 4;
case 2: return 5;
default:
/* Invalid contact (e.g. palm). Ignore it. */
return 0;
}
}
return finger_count;
}
static int cypress_parse_packet(struct psmouse *psmouse,
struct cytp_data *cytp,
struct cytp_report_data *report_data)
{
u8 *packet = psmouse->packet;
u8 header_byte = packet[0];
memset(report_data, 0, sizeof(struct cytp_report_data));
report_data->contact_cnt = cypress_get_finger_count(header_byte);
report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
if (report_data->contact_cnt == 1) {
report_data->contacts[0].x =
((packet[1] & 0x70) << 4) | packet[2];
report_data->contacts[0].y =
((packet[1] & 0x07) << 8) | packet[3];
if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
report_data->contacts[0].z = packet[4];
} else if (report_data->contact_cnt >= 2) {
report_data->contacts[0].x =
((packet[1] & 0x70) << 4) | packet[2];
report_data->contacts[0].y =
((packet[1] & 0x07) << 8) | packet[3];
if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
report_data->contacts[0].z = packet[4];
report_data->contacts[1].x =
((packet[5] & 0xf0) << 4) | packet[6];
report_data->contacts[1].y =
((packet[5] & 0x0f) << 8) | packet[7];
if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
report_data->contacts[1].z = report_data->contacts[0].z;
}
report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
/*
* This is only true if one of the mouse buttons were tapped. Make
* sure it doesn't turn into a click. The regular tap-to-click
* functionality will handle that on its own. If we don't do this,
* disabling tap-to-click won't affect the mouse button zones.
*/
if (report_data->tap)
report_data->left = 0;
#ifdef CYTP_DEBUG_VERBOSE
{
int i;
int n = report_data->contact_cnt;
psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
psmouse_dbg(psmouse, "contact_cnt = %d\n",
report_data->contact_cnt);
if (n > CYTP_MAX_MT_SLOTS)
n = CYTP_MAX_MT_SLOTS;
for (i = 0; i < n; i++)
psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
report_data->contacts[i].x,
report_data->contacts[i].y,
report_data->contacts[i].z);
psmouse_dbg(psmouse, "left = %d\n", report_data->left);
psmouse_dbg(psmouse, "right = %d\n", report_data->right);
psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
}
#endif
return 0;
}
static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
{
int i;
struct input_dev *input = psmouse->dev;
struct cytp_data *cytp = psmouse->private;
struct cytp_report_data report_data;
struct cytp_contact *contact;
struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
int slots[CYTP_MAX_MT_SLOTS];
int n;
cypress_parse_packet(psmouse, cytp, &report_data);
n = report_data.contact_cnt;
if (n > CYTP_MAX_MT_SLOTS)
n = CYTP_MAX_MT_SLOTS;
for (i = 0; i < n; i++) {
contact = &report_data.contacts[i];
pos[i].x = contact->x;
pos[i].y = contact->y;
}
input_mt_assign_slots(input, slots, pos, n, 0);
for (i = 0; i < n; i++) {
contact = &report_data.contacts[i];
input_mt_slot(input, slots[i]);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_POSITION_X, contact->x);
input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
input_report_abs(input, ABS_MT_PRESSURE, contact->z);
}
input_mt_sync_frame(input);
input_mt_report_finger_count(input, report_data.contact_cnt);
input_report_key(input, BTN_LEFT, report_data.left);
input_report_key(input, BTN_RIGHT, report_data.right);
input_report_key(input, BTN_MIDDLE, report_data.middle);
input_sync(input);
}
static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
{
int contact_cnt;
int index = psmouse->pktcnt - 1;
u8 *packet = psmouse->packet;
struct cytp_data *cytp = psmouse->private;
if (index < 0 || index > cytp->pkt_size)
return PSMOUSE_BAD_DATA;
if (index == 0 && (packet[0] & 0xfc) == 0) {
/* call packet process for reporting finger leave. */
cypress_process_packet(psmouse, 1);
return PSMOUSE_FULL_PACKET;
}
/*
* Perform validation (and adjust packet size) based only on the
* first byte; allow all further bytes through.
*/
if (index != 0)
return PSMOUSE_GOOD_DATA;
/*
* If absolute/relative mode bit has not been set yet, just pass
* the byte through.
*/
if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
return PSMOUSE_GOOD_DATA;
if ((packet[0] & 0x08) == 0x08)
return PSMOUSE_BAD_DATA;
contact_cnt = cypress_get_finger_count(packet[0]);
if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
else
cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
return PSMOUSE_GOOD_DATA;
}
static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
{
struct cytp_data *cytp = psmouse->private;
if (psmouse->pktcnt >= cytp->pkt_size) {
cypress_process_packet(psmouse, 0);
return PSMOUSE_FULL_PACKET;
}
return cypress_validate_byte(psmouse);
}
static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
{
struct cytp_data *cytp = psmouse->private;
u8 rate_param;
if (rate >= 80) {
psmouse->rate = 80;
cytp->mode |= CYTP_BIT_HIGH_RATE;
} else {
psmouse->rate = 40;
cytp->mode &= ~CYTP_BIT_HIGH_RATE;
}
rate_param = (u8)rate;
ps2_command(&psmouse->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE);
}
static void cypress_disconnect(struct psmouse *psmouse)
{
cypress_reset(psmouse);
kfree(psmouse->private);
psmouse->private = NULL;
}
static int cypress_reconnect(struct psmouse *psmouse)
{
int tries = CYTP_PS2_CMD_TRIES;
int error;
do {
cypress_reset(psmouse);
error = cypress_detect(psmouse, false);
} while (error && (--tries > 0));
if (error) {
psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
return error;
}
error = cypress_set_absolute_mode(psmouse);
if (error) {
psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
return error;
}
return 0;
}
int cypress_init(struct psmouse *psmouse)
{
struct cytp_data *cytp;
int error;
cytp = kzalloc_obj(*cytp);
if (!cytp)
return -ENOMEM;
psmouse->private = cytp;
psmouse->pktsize = 8;
cypress_reset(psmouse);
error = cypress_query_hardware(psmouse);
if (error) {
psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
goto err_exit;
}
error = cypress_set_absolute_mode(psmouse);
if (error) {
psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
goto err_exit;
}
error = cypress_set_input_params(psmouse->dev, cytp);
if (error) {
psmouse_err(psmouse, "init: Unable to set input params.\n");
goto err_exit;
}
psmouse->model = 1;
psmouse->protocol_handler = cypress_protocol_handler;
psmouse->set_rate = cypress_set_rate;
psmouse->disconnect = cypress_disconnect;
psmouse->reconnect = cypress_reconnect;
psmouse->cleanup = cypress_reset;
psmouse->resync_time = 0;
return 0;
err_exit:
/*
* Reset Cypress Trackpad as a standard mouse. Then
* let psmouse driver communicating with it as default PS2 mouse.
*/
cypress_reset(psmouse);
psmouse->private = NULL;
kfree(cytp);
return error;
}
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _CYPRESS_PS2_H
#define _CYPRESS_PS2_H
#include "psmouse.h"
#define CMD_BITS_MASK 0x03
#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
#define ENCODE_CMD(aa, bb, cc, dd) \
(COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0)
#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1)
#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0)
#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */
#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */
#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0)
#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1)
#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w))
#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0)
#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w))
#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0)
#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e))
#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0)
#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s))
#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0)
#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s))
#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1)
#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3)
#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
/* Cypress trackpad working mode. */
#define CYTP_BIT_ABS_PRESSURE (1 << 3)
#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
#define CYTP_BIT_CYPRESS_REL (1 << 1)
#define CYTP_BIT_STANDARD_REL (1 << 0)
#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
#define CYTP_BIT_HIGH_RATE (1 << 4)
/*
* report mode bit is set, firmware working in Remote Mode.
* report mode bit is cleared, firmware working in Stream Mode.
*/
#define CYTP_BIT_REPORT_MODE (1 << 5)
/* scrolling width values for set HSCROLL and VSCROLL width command. */
#define SCROLL_WIDTH_NARROW 1
#define SCROLL_WIDTH_NORMAL 2
#define SCROLL_WIDTH_WIDE 3
#define PALM_GEOMETRY_ENABLE 1
#define PALM_GEOMETRY_DISABLE 0
#define TP_METRICS_MASK 0x80
#define FW_VERSION_MASX 0x7f
#define FW_VER_HIGH_MASK 0x70
#define FW_VER_LOW_MASK 0x0f
/* Times to retry a ps2_command and millisecond delay between tries. */
#define CYTP_PS2_CMD_TRIES 3
#define CYTP_PS2_CMD_DELAY 500
/* time out for PS/2 command only in milliseconds. */
#define CYTP_CMD_TIMEOUT 200
#define CYTP_DATA_TIMEOUT 30
#define CYTP_EXT_CMD 0xe8
#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */
#define CYTP_105001_HIGH 59
#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
#define CYTP_DEFAULT_HIGH (CYTP_105001_HIGH)
#define CYTP_ABS_MAX_X 1600
#define CYTP_ABS_MAX_Y 900
#define CYTP_MAX_PRESSURE 255
#define CYTP_MIN_PRESSURE 0
/* header byte bits of relative package. */
#define BTN_LEFT_BIT 0x01
#define BTN_RIGHT_BIT 0x02
#define BTN_MIDDLE_BIT 0x04
#define REL_X_SIGN_BIT 0x10
#define REL_Y_SIGN_BIT 0x20
/* header byte bits of absolute package. */
#define ABS_VSCROLL_BIT 0x10
#define ABS_HSCROLL_BIT 0x20
#define ABS_MULTIFINGER_TAP 0x04
#define ABS_EDGE_MOTION_MASK 0x80
#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */
#define DFLT_RESP_SMBUS_BIT 0x80
#define DFLT_SMBUS_MODE 0x80
#define DFLT_PS2_MODE 0x00
#define DFLT_RESP_BIT_MODE 0x40
#define DFLT_RESP_REMOTE_MODE 0x40
#define DFLT_RESP_STREAM_MODE 0x00
#define DFLT_RESP_BIT_REPORTING 0x20
#define DFLT_RESP_BIT_SCALING 0x10
#define TP_METRICS_BIT_PALM 0x80
#define TP_METRICS_BIT_STUBBORN 0x40
#define TP_METRICS_BIT_2F_JITTER 0x30
#define TP_METRICS_BIT_1F_JITTER 0x0c
#define TP_METRICS_BIT_APA 0x02
#define TP_METRICS_BIT_MTG 0x01
#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0
#define TP_METRICS_BIT_2F_SPIKE 0x0c
#define TP_METRICS_BIT_1F_SPIKE 0x03
/* bits of first byte response of E9h-Status Request command. */
#define RESP_BTN_RIGHT_BIT 0x01
#define RESP_BTN_MIDDLE_BIT 0x02
#define RESP_BTN_LEFT_BIT 0x04
#define RESP_SCALING_BIT 0x10
#define RESP_ENABLE_BIT 0x20
#define RESP_REMOTE_BIT 0x40
#define RESP_SMBUS_BIT 0x80
#define CYTP_MAX_MT_SLOTS 2
struct cytp_contact {
int x;
int y;
int z; /* also named as touch pressure. */
};
/* The structure of Cypress Trackpad event data. */
struct cytp_report_data {
int contact_cnt;
struct cytp_contact contacts[CYTP_MAX_MT_SLOTS];
unsigned int left:1;
unsigned int right:1;
unsigned int middle:1;
unsigned int tap:1; /* multi-finger tap detected. */
};
/* The structure of Cypress Trackpad device private data. */
struct cytp_data {
int fw_version;
int pkt_size;
int mode;
int tp_min_pressure;
int tp_max_pressure;
int tp_width; /* X direction physical size in mm. */
int tp_high; /* Y direction physical size in mm. */
int tp_max_abs_x; /* Max X absolute units that can be reported. */
int tp_max_abs_y; /* Max Y absolute units that can be reported. */
int tp_res_x; /* X resolution in units/mm. */
int tp_res_y; /* Y resolution in units/mm. */
int tp_metrics_supported;
};
int cypress_detect(struct psmouse *psmouse, bool set_properties);
int cypress_init(struct psmouse *psmouse);
#endif /* _CYPRESS_PS2_H */
+121
View File
@@ -0,0 +1,121 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Elan I2C/SMBus Touchpad driver
*
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
* copyright (c) 2011-2012 Google, Inc.
*
* Trademarks are the property of their respective owners.
*/
#ifndef _ELAN_I2C_H
#define _ELAN_I2C_H
#include <linux/types.h>
#define ETP_ENABLE_ABS 0x0001
#define ETP_ENABLE_CALIBRATE 0x0002
#define ETP_DISABLE_CALIBRATE 0x0000
#define ETP_DISABLE_POWER 0x0001
#define ETP_PRESSURE_OFFSET 25
#define ETP_CALIBRATE_MAX_LEN 3
#define ETP_FEATURE_REPORT_MK BIT(0)
#define ETP_REPORT_ID 0x5D
#define ETP_TP_REPORT_ID 0x5E
#define ETP_TP_REPORT_ID2 0x5F
#define ETP_REPORT_ID2 0x60 /* High precision report */
#define ETP_REPORT_ID_OFFSET 2
#define ETP_TOUCH_INFO_OFFSET 3
#define ETP_FINGER_DATA_OFFSET 4
#define ETP_HOVER_INFO_OFFSET 30
#define ETP_MK_DATA_OFFSET 33 /* For high precision reports */
#define ETP_MAX_REPORT_LEN 39
#define ETP_MAX_FINGERS 5
#define ETP_FINGER_DATA_LEN 5
/* IAP Firmware handling */
#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0"
#define ETP_FW_NAME "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin"
#define ETP_IAP_START_ADDR 0x0083
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
#define ETP_FW_IAP_INTF_ERR (1 << 4)
#define ETP_FW_PAGE_SIZE 64
#define ETP_FW_PAGE_SIZE_128 128
#define ETP_FW_PAGE_SIZE_512 512
#define ETP_FW_SIGNATURE_SIZE 6
#define ETP_PRODUCT_ID_WHITEBOX 0x00B8
#define ETP_PRODUCT_ID_VOXEL 0x00BF
#define ETP_PRODUCT_ID_DELBIN 0x00C2
#define ETP_PRODUCT_ID_MAGPIE 0x0120
#define ETP_PRODUCT_ID_BOBBA 0x0121
struct i2c_client;
struct completion;
enum tp_mode {
IAP_MODE = 1,
MAIN_MODE
};
struct elan_transport_ops {
int (*initialize)(struct i2c_client *client);
int (*sleep_control)(struct i2c_client *, bool sleep);
int (*power_control)(struct i2c_client *, bool enable);
int (*set_mode)(struct i2c_client *client, u8 mode);
int (*calibrate)(struct i2c_client *client);
int (*calibrate_result)(struct i2c_client *client, u8 *val);
int (*get_baseline_data)(struct i2c_client *client,
bool max_baseline, u8 *value);
int (*get_version)(struct i2c_client *client, u8 pattern, bool iap,
u8 *version);
int (*get_sm_version)(struct i2c_client *client, u8 pattern,
u16 *ic_type, u8 *version, u8 *clickpad);
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
int (*get_product_id)(struct i2c_client *client, u16 *id);
int (*get_max)(struct i2c_client *client,
unsigned int *max_x, unsigned int *max_y);
int (*get_resolution)(struct i2c_client *client,
u8 *hw_res_x, u8 *hw_res_y);
int (*get_num_traces)(struct i2c_client *client,
unsigned int *x_tracenum,
unsigned int *y_tracenum);
int (*iap_get_mode)(struct i2c_client *client, enum tp_mode *mode);
int (*iap_reset)(struct i2c_client *client);
int (*prepare_fw_update)(struct i2c_client *client, u16 ic_type,
u8 iap_version, u16 fw_page_size);
int (*write_fw_block)(struct i2c_client *client, u16 fw_page_size,
const u8 *page, u16 checksum, int idx);
int (*finish_fw_update)(struct i2c_client *client,
struct completion *reset_done);
int (*get_report_features)(struct i2c_client *client, u8 pattern,
unsigned int *features,
unsigned int *report_len);
int (*get_report)(struct i2c_client *client, u8 *report,
unsigned int report_len);
int (*get_pressure_adjustment)(struct i2c_client *client,
int *adjustment);
int (*get_pattern)(struct i2c_client *client, u8 *pattern);
};
extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
#endif /* _ELAN_I2C_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,777 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Elan I2C/SMBus Touchpad driver - I2C interface
*
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
* copyright (c) 2011-2012 Google, Inc.
*
* Trademarks are the property of their respective owners.
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/unaligned.h>
#include "elan_i2c.h"
/* Elan i2c commands */
#define ETP_I2C_RESET 0x0100
#define ETP_I2C_WAKE_UP 0x0800
#define ETP_I2C_SLEEP 0x0801
#define ETP_I2C_DESC_CMD 0x0001
#define ETP_I2C_REPORT_DESC_CMD 0x0002
#define ETP_I2C_STAND_CMD 0x0005
#define ETP_I2C_PATTERN_CMD 0x0100
#define ETP_I2C_UNIQUEID_CMD 0x0101
#define ETP_I2C_FW_VERSION_CMD 0x0102
#define ETP_I2C_IC_TYPE_CMD 0x0103
#define ETP_I2C_OSM_VERSION_CMD 0x0103
#define ETP_I2C_NSM_VERSION_CMD 0x0104
#define ETP_I2C_XY_TRACENUM_CMD 0x0105
#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
#define ETP_I2C_RESOLUTION_CMD 0x0108
#define ETP_I2C_PRESSURE_CMD 0x010A
#define ETP_I2C_IAP_VERSION_CMD 0x0110
#define ETP_I2C_IC_TYPE_P0_CMD 0x0110
#define ETP_I2C_IAP_VERSION_P0_CMD 0x0111
#define ETP_I2C_SET_CMD 0x0300
#define ETP_I2C_POWER_CMD 0x0307
#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
#define ETP_I2C_IAP_CTRL_CMD 0x0310
#define ETP_I2C_IAP_CMD 0x0311
#define ETP_I2C_IAP_RESET_CMD 0x0314
#define ETP_I2C_IAP_CHECKSUM_CMD 0x0315
#define ETP_I2C_CALIBRATE_CMD 0x0316
#define ETP_I2C_MAX_BASELINE_CMD 0x0317
#define ETP_I2C_MIN_BASELINE_CMD 0x0318
#define ETP_I2C_IAP_TYPE_REG 0x0040
#define ETP_I2C_IAP_TYPE_CMD 0x0304
#define ETP_I2C_REPORT_LEN 34
#define ETP_I2C_REPORT_LEN_ID2 39
#define ETP_I2C_REPORT_MAX_LEN 39
#define ETP_I2C_DESC_LENGTH 30
#define ETP_I2C_REPORT_DESC_LENGTH 158
#define ETP_I2C_INF_LENGTH 2
#define ETP_I2C_IAP_PASSWORD 0x1EA5
#define ETP_I2C_IAP_RESET 0xF0F0
#define ETP_I2C_MAIN_MODE_ON (1 << 9)
#define ETP_I2C_IAP_REG_L 0x01
#define ETP_I2C_IAP_REG_H 0x06
static int elan_i2c_read_block(struct i2c_client *client,
u16 reg, u8 *val, u16 len)
{
__le16 buf[] = {
cpu_to_le16(reg),
};
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = client->flags & I2C_M_TEN,
.len = sizeof(buf),
.buf = (u8 *)buf,
},
{
.addr = client->addr,
.flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
.len = len,
.buf = val,
}
};
int ret;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
return ret == ARRAY_SIZE(msgs) ? 0 : (ret < 0 ? ret : -EIO);
}
static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
{
int retval;
retval = elan_i2c_read_block(client, reg, val, ETP_I2C_INF_LENGTH);
if (retval < 0) {
dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
return retval;
}
return 0;
}
static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
{
__le16 buf[] = {
cpu_to_le16(reg),
cpu_to_le16(cmd),
};
struct i2c_msg msg = {
.addr = client->addr,
.flags = client->flags & I2C_M_TEN,
.len = sizeof(buf),
.buf = (u8 *)buf,
};
int ret;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret != 1) {
if (ret >= 0)
ret = -EIO;
dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n",
reg, ret);
return ret;
}
return 0;
}
static int elan_i2c_initialize(struct i2c_client *client)
{
struct device *dev = &client->dev;
int error;
u8 val[256];
error = elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, ETP_I2C_RESET);
if (error) {
dev_err(dev, "device reset failed: %d\n", error);
return error;
}
/* Wait for the device to reset */
msleep(100);
/* get reset acknowledgement 0000 */
error = i2c_master_recv(client, val, ETP_I2C_INF_LENGTH);
if (error < 0) {
dev_err(dev, "failed to read reset response: %d\n", error);
return error;
}
error = elan_i2c_read_block(client, ETP_I2C_DESC_CMD,
val, ETP_I2C_DESC_LENGTH);
if (error) {
dev_err(dev, "cannot get device descriptor: %d\n", error);
return error;
}
error = elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
val, ETP_I2C_REPORT_DESC_LENGTH);
if (error) {
dev_err(dev, "fetching report descriptor failed.: %d\n", error);
return error;
}
return 0;
}
static int elan_i2c_sleep_control(struct i2c_client *client, bool sleep)
{
return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
sleep ? ETP_I2C_SLEEP : ETP_I2C_WAKE_UP);
}
static int elan_i2c_power_control(struct i2c_client *client, bool enable)
{
u8 val[2];
u16 reg;
int error;
error = elan_i2c_read_cmd(client, ETP_I2C_POWER_CMD, val);
if (error) {
dev_err(&client->dev,
"failed to read current power state: %d\n",
error);
return error;
}
reg = le16_to_cpup((__le16 *)val);
if (enable)
reg &= ~ETP_DISABLE_POWER;
else
reg |= ETP_DISABLE_POWER;
error = elan_i2c_write_cmd(client, ETP_I2C_POWER_CMD, reg);
if (error) {
dev_err(&client->dev,
"failed to write current power state: %d\n",
error);
return error;
}
return 0;
}
static int elan_i2c_set_mode(struct i2c_client *client, u8 mode)
{
return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD, mode);
}
static int elan_i2c_calibrate(struct i2c_client *client)
{
return elan_i2c_write_cmd(client, ETP_I2C_CALIBRATE_CMD, 1);
}
static int elan_i2c_calibrate_result(struct i2c_client *client, u8 *val)
{
return elan_i2c_read_block(client, ETP_I2C_CALIBRATE_CMD, val, 1);
}
static int elan_i2c_get_baseline_data(struct i2c_client *client,
bool max_baseline, u8 *value)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client,
max_baseline ? ETP_I2C_MAX_BASELINE_CMD :
ETP_I2C_MIN_BASELINE_CMD,
val);
if (error)
return error;
*value = le16_to_cpup((__le16 *)val);
return 0;
}
static int elan_i2c_get_pattern(struct i2c_client *client, u8 *pattern)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_PATTERN_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get pattern: %d\n", error);
return error;
}
/*
* Not all versions of firmware implement "get pattern" command.
* When this command is not implemented the device will respond
* with 0xFF 0xFF, which we will treat as "old" pattern 0.
*/
*pattern = val[0] == 0xFF && val[1] == 0xFF ? 0 : val[1];
return 0;
}
static int elan_i2c_get_version(struct i2c_client *client,
u8 pattern, bool iap, u8 *version)
{
int error;
u16 cmd;
u8 val[3];
if (!iap)
cmd = ETP_I2C_FW_VERSION_CMD;
else if (pattern == 0)
cmd = ETP_I2C_IAP_VERSION_P0_CMD;
else
cmd = ETP_I2C_IAP_VERSION_CMD;
error = elan_i2c_read_cmd(client, cmd, val);
if (error) {
dev_err(&client->dev, "failed to get %s version: %d\n",
iap ? "IAP" : "FW", error);
return error;
}
if (pattern >= 0x01)
*version = iap ? val[1] : val[0];
else
*version = val[0];
return 0;
}
static int elan_i2c_get_sm_version(struct i2c_client *client, u8 pattern,
u16 *ic_type, u8 *version, u8 *clickpad)
{
int error;
u8 val[3];
if (pattern >= 0x01) {
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get ic type: %d\n",
error);
return error;
}
*ic_type = be16_to_cpup((__be16 *)val);
error = elan_i2c_read_cmd(client, ETP_I2C_NSM_VERSION_CMD,
val);
if (error) {
dev_err(&client->dev, "failed to get SM version: %d\n",
error);
return error;
}
*version = val[1];
*clickpad = val[0] & 0x10;
} else {
error = elan_i2c_read_cmd(client, ETP_I2C_OSM_VERSION_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get SM version: %d\n",
error);
return error;
}
*version = val[0];
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_P0_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get ic type: %d\n",
error);
return error;
}
*ic_type = val[0];
error = elan_i2c_read_cmd(client, ETP_I2C_NSM_VERSION_CMD,
val);
if (error) {
dev_err(&client->dev, "failed to get SM version: %d\n",
error);
return error;
}
*clickpad = val[0] & 0x10;
}
return 0;
}
static int elan_i2c_get_product_id(struct i2c_client *client, u16 *id)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_UNIQUEID_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get product ID: %d\n", error);
return error;
}
*id = le16_to_cpup((__le16 *)val);
return 0;
}
static int elan_i2c_get_checksum(struct i2c_client *client,
bool iap, u16 *csum)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client,
iap ? ETP_I2C_IAP_CHECKSUM_CMD :
ETP_I2C_FW_CHECKSUM_CMD,
val);
if (error) {
dev_err(&client->dev, "failed to get %s checksum: %d\n",
iap ? "IAP" : "FW", error);
return error;
}
*csum = le16_to_cpup((__le16 *)val);
return 0;
}
static int elan_i2c_get_max(struct i2c_client *client,
unsigned int *max_x, unsigned int *max_y)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_MAX_X_AXIS_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get X dimension: %d\n", error);
return error;
}
*max_x = le16_to_cpup((__le16 *)val);
error = elan_i2c_read_cmd(client, ETP_I2C_MAX_Y_AXIS_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get Y dimension: %d\n", error);
return error;
}
*max_y = le16_to_cpup((__le16 *)val);
return 0;
}
static int elan_i2c_get_resolution(struct i2c_client *client,
u8 *hw_res_x, u8 *hw_res_y)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_RESOLUTION_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get resolution: %d\n", error);
return error;
}
*hw_res_x = val[0];
*hw_res_y = val[1];
return 0;
}
static int elan_i2c_get_num_traces(struct i2c_client *client,
unsigned int *x_traces,
unsigned int *y_traces)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_XY_TRACENUM_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get trace info: %d\n", error);
return error;
}
*x_traces = val[0];
*y_traces = val[1];
return 0;
}
static int elan_i2c_get_pressure_adjustment(struct i2c_client *client,
int *adjustment)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_PRESSURE_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get pressure format: %d\n",
error);
return error;
}
if ((val[0] >> 4) & 0x1)
*adjustment = 0;
else
*adjustment = ETP_PRESSURE_OFFSET;
return 0;
}
static int elan_i2c_iap_get_mode(struct i2c_client *client, enum tp_mode *mode)
{
int error;
u16 constant;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
if (error) {
dev_err(&client->dev,
"failed to read iap control register: %d\n",
error);
return error;
}
constant = le16_to_cpup((__le16 *)val);
dev_dbg(&client->dev, "iap control reg: 0x%04x.\n", constant);
*mode = (constant & ETP_I2C_MAIN_MODE_ON) ? MAIN_MODE : IAP_MODE;
return 0;
}
static int elan_i2c_iap_reset(struct i2c_client *client)
{
int error;
error = elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD,
ETP_I2C_IAP_RESET);
if (error) {
dev_err(&client->dev, "cannot reset IC: %d\n", error);
return error;
}
return 0;
}
static int elan_i2c_set_flash_key(struct i2c_client *client)
{
int error;
error = elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD,
ETP_I2C_IAP_PASSWORD);
if (error) {
dev_err(&client->dev, "cannot set flash key: %d\n", error);
return error;
}
return 0;
}
static int elan_read_write_iap_type(struct i2c_client *client, u16 fw_page_size)
{
int error;
u16 constant;
u8 val[3];
int retry = 3;
do {
error = elan_i2c_write_cmd(client, ETP_I2C_IAP_TYPE_CMD,
fw_page_size / 2);
if (error) {
dev_err(&client->dev,
"cannot write iap type: %d\n", error);
return error;
}
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_TYPE_CMD, val);
if (error) {
dev_err(&client->dev,
"failed to read iap type register: %d\n",
error);
return error;
}
constant = le16_to_cpup((__le16 *)val);
dev_dbg(&client->dev, "iap type reg: 0x%04x\n", constant);
if (constant == fw_page_size / 2)
return 0;
} while (--retry > 0);
dev_err(&client->dev, "cannot set iap type\n");
return -EIO;
}
static int elan_i2c_prepare_fw_update(struct i2c_client *client, u16 ic_type,
u8 iap_version, u16 fw_page_size)
{
struct device *dev = &client->dev;
int error;
enum tp_mode mode;
u8 val[3];
u16 password;
/* Get FW in which mode (IAP_MODE/MAIN_MODE) */
error = elan_i2c_iap_get_mode(client, &mode);
if (error)
return error;
if (mode == IAP_MODE) {
/* Reset IC */
error = elan_i2c_iap_reset(client);
if (error)
return error;
msleep(30);
}
/* Set flash key*/
error = elan_i2c_set_flash_key(client);
if (error)
return error;
/* Wait for F/W IAP initialization */
msleep(mode == MAIN_MODE ? 100 : 30);
/* Check if we are in IAP mode or not */
error = elan_i2c_iap_get_mode(client, &mode);
if (error)
return error;
if (mode == MAIN_MODE) {
dev_err(dev, "wrong mode: %d\n", mode);
return -EIO;
}
if (ic_type >= 0x0D && iap_version >= 1) {
error = elan_read_write_iap_type(client, fw_page_size);
if (error)
return error;
}
/* Set flash key again */
error = elan_i2c_set_flash_key(client);
if (error)
return error;
/* Wait for F/W IAP initialization */
msleep(30);
/* read back to check we actually enabled successfully. */
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val);
if (error) {
dev_err(dev, "cannot read iap password: %d\n",
error);
return error;
}
password = le16_to_cpup((__le16 *)val);
if (password != ETP_I2C_IAP_PASSWORD) {
dev_err(dev, "wrong iap password: 0x%X\n", password);
return -EIO;
}
return 0;
}
static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size,
const u8 *page, u16 checksum, int idx)
{
struct device *dev = &client->dev;
u8 val[3];
u16 result;
int ret, error;
u8 *page_store __free(kfree) = kmalloc(fw_page_size + 4, GFP_KERNEL);
if (!page_store)
return -ENOMEM;
page_store[0] = ETP_I2C_IAP_REG_L;
page_store[1] = ETP_I2C_IAP_REG_H;
memcpy(&page_store[2], page, fw_page_size);
/* recode checksum at last two bytes */
put_unaligned_le16(checksum, &page_store[fw_page_size + 2]);
ret = i2c_master_send(client, page_store, fw_page_size + 4);
if (ret != fw_page_size + 4) {
error = ret < 0 ? ret : -EIO;
dev_err(dev, "Failed to write page %d: %d\n", idx, error);
return error;
}
/* Wait for F/W to update one page ROM data. */
msleep(fw_page_size == ETP_FW_PAGE_SIZE_512 ? 50 : 35);
error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
if (error) {
dev_err(dev, "Failed to read IAP write result: %d\n", error);
return error;
}
result = le16_to_cpup((__le16 *)val);
if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
dev_err(dev, "IAP reports failed write: %04hx\n",
result);
return -EIO;
}
return 0;
}
static int elan_i2c_finish_fw_update(struct i2c_client *client,
struct completion *completion)
{
struct device *dev = &client->dev;
int error = 0;
int len;
u8 buffer[ETP_I2C_REPORT_MAX_LEN];
len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_MAX_LEN);
if (len <= 0) {
error = len < 0 ? len : -EIO;
dev_warn(dev, "failed to read I2C data after FW WDT reset: %d (%d)\n",
error, len);
}
reinit_completion(completion);
enable_irq(client->irq);
error = elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, ETP_I2C_RESET);
if (error) {
dev_err(dev, "device reset failed: %d\n", error);
} else if (!wait_for_completion_timeout(completion,
msecs_to_jiffies(700))) {
dev_err(dev, "timeout waiting for device reset\n");
error = -ETIMEDOUT;
}
disable_irq(client->irq);
if (error)
return error;
len = i2c_master_recv(client, buffer, ETP_I2C_INF_LENGTH);
if (len != ETP_I2C_INF_LENGTH) {
error = len < 0 ? len : -EIO;
dev_err(dev, "failed to read INT signal: %d (%d)\n",
error, len);
return error;
}
return 0;
}
static int elan_i2c_get_report_features(struct i2c_client *client, u8 pattern,
unsigned int *features,
unsigned int *report_len)
{
*features = ETP_FEATURE_REPORT_MK;
*report_len = pattern <= 0x01 ?
ETP_I2C_REPORT_LEN : ETP_I2C_REPORT_LEN_ID2;
return 0;
}
static int elan_i2c_get_report(struct i2c_client *client,
u8 *report, unsigned int report_len)
{
int len;
len = i2c_master_recv(client, report, report_len);
if (len < 0) {
dev_err(&client->dev, "failed to read report data: %d\n", len);
return len;
}
if (len != report_len) {
dev_err(&client->dev,
"wrong report length (%d vs %d expected)\n",
len, report_len);
return -EIO;
}
return 0;
}
const struct elan_transport_ops elan_i2c_ops = {
.initialize = elan_i2c_initialize,
.sleep_control = elan_i2c_sleep_control,
.power_control = elan_i2c_power_control,
.set_mode = elan_i2c_set_mode,
.calibrate = elan_i2c_calibrate,
.calibrate_result = elan_i2c_calibrate_result,
.get_baseline_data = elan_i2c_get_baseline_data,
.get_version = elan_i2c_get_version,
.get_sm_version = elan_i2c_get_sm_version,
.get_product_id = elan_i2c_get_product_id,
.get_checksum = elan_i2c_get_checksum,
.get_pressure_adjustment = elan_i2c_get_pressure_adjustment,
.get_max = elan_i2c_get_max,
.get_resolution = elan_i2c_get_resolution,
.get_num_traces = elan_i2c_get_num_traces,
.iap_get_mode = elan_i2c_iap_get_mode,
.iap_reset = elan_i2c_iap_reset,
.prepare_fw_update = elan_i2c_prepare_fw_update,
.write_fw_block = elan_i2c_write_fw_block,
.finish_fw_update = elan_i2c_finish_fw_update,
.get_pattern = elan_i2c_get_pattern,
.get_report_features = elan_i2c_get_report_features,
.get_report = elan_i2c_get_report,
};
@@ -0,0 +1,558 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Elan I2C/SMBus Touchpad driver - SMBus interface
*
* Copyright (c) 2013 ELAN Microelectronics Corp.
*
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
*
* Based on cyapa driver:
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
* copyright (c) 2011-2012 Google, Inc.
*
* Trademarks are the property of their respective owners.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include "elan_i2c.h"
/* Elan SMbus commands */
#define ETP_SMBUS_IAP_CMD 0x00
#define ETP_SMBUS_ENABLE_TP 0x20
#define ETP_SMBUS_SLEEP_CMD 0x21
#define ETP_SMBUS_IAP_PASSWORD_WRITE 0x29
#define ETP_SMBUS_IAP_PASSWORD_READ 0x80
#define ETP_SMBUS_WRITE_FW_BLOCK 0x2A
#define ETP_SMBUS_IAP_RESET_CMD 0x2B
#define ETP_SMBUS_RANGE_CMD 0xA0
#define ETP_SMBUS_FW_VERSION_CMD 0xA1
#define ETP_SMBUS_XY_TRACENUM_CMD 0xA2
#define ETP_SMBUS_SM_VERSION_CMD 0xA3
#define ETP_SMBUS_UNIQUEID_CMD 0xA3
#define ETP_SMBUS_RESOLUTION_CMD 0xA4
#define ETP_SMBUS_HELLOPACKET_CMD 0xA7
#define ETP_SMBUS_PACKET_QUERY 0xA8
#define ETP_SMBUS_IAP_VERSION_CMD 0xAC
#define ETP_SMBUS_IAP_CTRL_CMD 0xAD
#define ETP_SMBUS_IAP_CHECKSUM_CMD 0xAE
#define ETP_SMBUS_FW_CHECKSUM_CMD 0xAF
#define ETP_SMBUS_MAX_BASELINE_CMD 0xC3
#define ETP_SMBUS_MIN_BASELINE_CMD 0xC4
#define ETP_SMBUS_CALIBRATE_QUERY 0xC5
#define ETP_SMBUS_REPORT_LEN 32
#define ETP_SMBUS_REPORT_LEN2 7
#define ETP_SMBUS_REPORT_OFFSET 2
#define ETP_SMBUS_HELLOPACKET_LEN 5
#define ETP_SMBUS_IAP_PASSWORD 0x1234
#define ETP_SMBUS_IAP_MODE_ON (1 << 6)
static int elan_smbus_initialize(struct i2c_client *client)
{
u8 check[ETP_SMBUS_HELLOPACKET_LEN] = { 0x55, 0x55, 0x55, 0x55, 0x55 };
u8 values[I2C_SMBUS_BLOCK_MAX] = {0};
int len, error;
/* Get hello packet */
len = i2c_smbus_read_block_data(client,
ETP_SMBUS_HELLOPACKET_CMD, values);
if (len != ETP_SMBUS_HELLOPACKET_LEN) {
dev_err(&client->dev, "hello packet length fail: %d\n", len);
error = len < 0 ? len : -EIO;
return error;
}
/* compare hello packet */
if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
dev_err(&client->dev, "hello packet fail [%*ph]\n",
ETP_SMBUS_HELLOPACKET_LEN, values);
return -ENXIO;
}
/* enable tp */
error = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
if (error) {
dev_err(&client->dev, "failed to enable touchpad: %d\n", error);
return error;
}
return 0;
}
static int elan_smbus_set_mode(struct i2c_client *client, u8 mode)
{
u8 cmd[4] = { 0x00, 0x07, 0x00, mode };
return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
sizeof(cmd), cmd);
}
static int elan_smbus_sleep_control(struct i2c_client *client, bool sleep)
{
if (sleep)
return i2c_smbus_write_byte(client, ETP_SMBUS_SLEEP_CMD);
else
return 0; /* XXX should we send ETP_SMBUS_ENABLE_TP here? */
}
static int elan_smbus_power_control(struct i2c_client *client, bool enable)
{
return 0; /* A no-op */
}
static int elan_smbus_calibrate(struct i2c_client *client)
{
u8 cmd[4] = { 0x00, 0x08, 0x00, 0x01 };
return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
sizeof(cmd), cmd);
}
static int elan_smbus_calibrate_result(struct i2c_client *client, u8 *val)
{
int error;
u8 buf[I2C_SMBUS_BLOCK_MAX] = {0};
BUILD_BUG_ON(ETP_CALIBRATE_MAX_LEN > sizeof(buf));
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_CALIBRATE_QUERY, buf);
if (error < 0)
return error;
memcpy(val, buf, ETP_CALIBRATE_MAX_LEN);
return 0;
}
static int elan_smbus_get_baseline_data(struct i2c_client *client,
bool max_baseline, u8 *value)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
max_baseline ?
ETP_SMBUS_MAX_BASELINE_CMD :
ETP_SMBUS_MIN_BASELINE_CMD,
val);
if (error < 0)
return error;
*value = be16_to_cpup((__be16 *)val);
return 0;
}
static int elan_smbus_get_version(struct i2c_client *client,
u8 pattern, bool iap, u8 *version)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
iap ? ETP_SMBUS_IAP_VERSION_CMD :
ETP_SMBUS_FW_VERSION_CMD,
val);
if (error < 0) {
dev_err(&client->dev, "failed to get %s version: %d\n",
iap ? "IAP" : "FW", error);
return error;
}
*version = val[2];
return 0;
}
static int elan_smbus_get_sm_version(struct i2c_client *client, u8 pattern,
u16 *ic_type, u8 *version, u8 *clickpad)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_SM_VERSION_CMD, val);
if (error < 0) {
dev_err(&client->dev, "failed to get SM version: %d\n", error);
return error;
}
*version = val[0];
*ic_type = val[1];
*clickpad = val[0] & 0x10;
return 0;
}
static int elan_smbus_get_product_id(struct i2c_client *client, u16 *id)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_UNIQUEID_CMD, val);
if (error < 0) {
dev_err(&client->dev, "failed to get product ID: %d\n", error);
return error;
}
*id = be16_to_cpup((__be16 *)val);
return 0;
}
static int elan_smbus_get_checksum(struct i2c_client *client,
bool iap, u16 *csum)
{
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client,
iap ? ETP_SMBUS_FW_CHECKSUM_CMD :
ETP_SMBUS_IAP_CHECKSUM_CMD,
val);
if (error < 0) {
dev_err(&client->dev, "failed to get %s checksum: %d\n",
iap ? "IAP" : "FW", error);
return error;
}
*csum = be16_to_cpup((__be16 *)val);
return 0;
}
static int elan_smbus_get_max(struct i2c_client *client,
unsigned int *max_x, unsigned int *max_y)
{
int ret;
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
if (ret != 3) {
error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to get dimensions: %d\n", error);
return error;
}
*max_x = (0x0f & val[0]) << 8 | val[1];
*max_y = (0xf0 & val[0]) << 4 | val[2];
return 0;
}
static int elan_smbus_get_resolution(struct i2c_client *client,
u8 *hw_res_x, u8 *hw_res_y)
{
int ret;
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val);
if (ret != 3) {
error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to get resolution: %d\n", error);
return error;
}
*hw_res_x = val[1] & 0x0F;
*hw_res_y = (val[1] & 0xF0) >> 4;
return 0;
}
static int elan_smbus_get_num_traces(struct i2c_client *client,
unsigned int *x_traces,
unsigned int *y_traces)
{
int ret;
int error;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val);
if (ret != 3) {
error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to get trace info: %d\n", error);
return error;
}
*x_traces = val[1];
*y_traces = val[2];
return 0;
}
static int elan_smbus_get_pressure_adjustment(struct i2c_client *client,
int *adjustment)
{
*adjustment = ETP_PRESSURE_OFFSET;
return 0;
}
static int elan_smbus_iap_get_mode(struct i2c_client *client,
enum tp_mode *mode)
{
int error;
u16 constant;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
error = i2c_smbus_read_block_data(client, ETP_SMBUS_IAP_CTRL_CMD, val);
if (error < 0) {
dev_err(&client->dev, "failed to read iap ctrol register: %d\n",
error);
return error;
}
constant = be16_to_cpup((__be16 *)val);
dev_dbg(&client->dev, "iap control reg: 0x%04x.\n", constant);
*mode = (constant & ETP_SMBUS_IAP_MODE_ON) ? IAP_MODE : MAIN_MODE;
return 0;
}
static int elan_smbus_iap_reset(struct i2c_client *client)
{
int error;
error = i2c_smbus_write_byte(client, ETP_SMBUS_IAP_RESET_CMD);
if (error) {
dev_err(&client->dev, "cannot reset IC: %d\n", error);
return error;
}
return 0;
}
static int elan_smbus_set_flash_key(struct i2c_client *client)
{
int error;
u8 cmd[4] = { 0x00, 0x0B, 0x00, 0x5A };
error = i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
sizeof(cmd), cmd);
if (error) {
dev_err(&client->dev, "cannot set flash key: %d\n", error);
return error;
}
return 0;
}
static int elan_smbus_prepare_fw_update(struct i2c_client *client, u16 ic_type,
u8 iap_version, u16 fw_page_size)
{
struct device *dev = &client->dev;
int len;
int error;
enum tp_mode mode;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
u16 password;
/* Get FW in which mode (IAP_MODE/MAIN_MODE) */
error = elan_smbus_iap_get_mode(client, &mode);
if (error)
return error;
if (mode == MAIN_MODE) {
/* set flash key */
error = elan_smbus_set_flash_key(client);
if (error)
return error;
/* write iap password */
if (i2c_smbus_write_byte(client,
ETP_SMBUS_IAP_PASSWORD_WRITE) < 0) {
dev_err(dev, "cannot write iap password\n");
return -EIO;
}
error = i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD,
sizeof(cmd), cmd);
if (error) {
dev_err(dev, "failed to write iap password: %d\n",
error);
return error;
}
/*
* Read back password to make sure we enabled flash
* successfully.
*/
len = i2c_smbus_read_block_data(client,
ETP_SMBUS_IAP_PASSWORD_READ,
val);
if (len < (int)sizeof(u16)) {
error = len < 0 ? len : -EIO;
dev_err(dev, "failed to read iap password: %d\n",
error);
return error;
}
password = be16_to_cpup((__be16 *)val);
if (password != ETP_SMBUS_IAP_PASSWORD) {
dev_err(dev, "wrong iap password = 0x%X\n", password);
return -EIO;
}
/* Wait 30ms for MAIN_MODE change to IAP_MODE */
msleep(30);
}
error = elan_smbus_set_flash_key(client);
if (error)
return error;
/* Reset IC */
error = elan_smbus_iap_reset(client);
if (error)
return error;
return 0;
}
static int elan_smbus_write_fw_block(struct i2c_client *client, u16 fw_page_size,
const u8 *page, u16 checksum, int idx)
{
struct device *dev = &client->dev;
int error;
u16 result;
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
/*
* Due to the limitation of smbus protocol limiting
* transfer to 32 bytes at a time, we must split block
* in 2 transfers.
*/
error = i2c_smbus_write_block_data(client,
ETP_SMBUS_WRITE_FW_BLOCK,
fw_page_size / 2,
page);
if (error) {
dev_err(dev, "Failed to write page %d (part %d): %d\n",
idx, 1, error);
return error;
}
error = i2c_smbus_write_block_data(client,
ETP_SMBUS_WRITE_FW_BLOCK,
fw_page_size / 2,
page + fw_page_size / 2);
if (error) {
dev_err(dev, "Failed to write page %d (part %d): %d\n",
idx, 2, error);
return error;
}
/* Wait for F/W to update one page ROM data. */
usleep_range(8000, 10000);
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_IAP_CTRL_CMD, val);
if (error < 0) {
dev_err(dev, "Failed to read IAP write result: %d\n",
error);
return error;
}
result = be16_to_cpup((__be16 *)val);
if (result & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) {
dev_err(dev, "IAP reports failed write: %04hx\n",
result);
return -EIO;
}
return 0;
}
static int elan_smbus_get_report_features(struct i2c_client *client, u8 pattern,
unsigned int *features,
unsigned int *report_len)
{
/*
* SMBus controllers with pattern 2 lack area info, as newer
* high-precision packets use that space for coordinates.
*/
*features = pattern <= 0x01 ? ETP_FEATURE_REPORT_MK : 0;
*report_len = ETP_SMBUS_REPORT_LEN;
return 0;
}
static int elan_smbus_get_report(struct i2c_client *client,
u8 *report, unsigned int report_len)
{
int len;
BUILD_BUG_ON(I2C_SMBUS_BLOCK_MAX > ETP_SMBUS_REPORT_LEN);
len = i2c_smbus_read_block_data(client,
ETP_SMBUS_PACKET_QUERY,
&report[ETP_SMBUS_REPORT_OFFSET]);
if (len < 0) {
dev_err(&client->dev, "failed to read report data: %d\n", len);
return len;
}
if (report[ETP_REPORT_ID_OFFSET] == ETP_TP_REPORT_ID2)
report_len = ETP_SMBUS_REPORT_LEN2;
if (len != report_len) {
dev_err(&client->dev,
"wrong report length (%d vs %d expected)\n",
len, report_len);
return -EIO;
}
return 0;
}
static int elan_smbus_finish_fw_update(struct i2c_client *client,
struct completion *fw_completion)
{
/* No special handling unlike I2C transport */
return 0;
}
static int elan_smbus_get_pattern(struct i2c_client *client, u8 *pattern)
{
*pattern = 0;
return 0;
}
const struct elan_transport_ops elan_smbus_ops = {
.initialize = elan_smbus_initialize,
.sleep_control = elan_smbus_sleep_control,
.power_control = elan_smbus_power_control,
.set_mode = elan_smbus_set_mode,
.calibrate = elan_smbus_calibrate,
.calibrate_result = elan_smbus_calibrate_result,
.get_baseline_data = elan_smbus_get_baseline_data,
.get_version = elan_smbus_get_version,
.get_sm_version = elan_smbus_get_sm_version,
.get_product_id = elan_smbus_get_product_id,
.get_checksum = elan_smbus_get_checksum,
.get_pressure_adjustment = elan_smbus_get_pressure_adjustment,
.get_max = elan_smbus_get_max,
.get_resolution = elan_smbus_get_resolution,
.get_num_traces = elan_smbus_get_num_traces,
.iap_get_mode = elan_smbus_iap_get_mode,
.iap_reset = elan_smbus_iap_reset,
.prepare_fw_update = elan_smbus_prepare_fw_update,
.write_fw_block = elan_smbus_write_fw_block,
.finish_fw_update = elan_smbus_finish_fw_update,
.get_report_features = elan_smbus_get_report_features,
.get_report = elan_smbus_get_report,
.get_pattern = elan_smbus_get_pattern,
};
File diff suppressed because it is too large Load Diff
+205
View File
@@ -0,0 +1,205 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Elantech Touchpad driver (v6)
*
* Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
*
* Trademarks are the property of their respective owners.
*/
#ifndef _ELANTECH_H
#define _ELANTECH_H
/*
* Command values for Synaptics style queries
*/
#define ETP_FW_ID_QUERY 0x00
#define ETP_FW_VERSION_QUERY 0x01
#define ETP_CAPABILITIES_QUERY 0x02
#define ETP_SAMPLE_QUERY 0x03
#define ETP_RESOLUTION_QUERY 0x04
#define ETP_ICBODY_QUERY 0x05
/*
* Command values for register reading or writing
*/
#define ETP_REGISTER_READ 0x10
#define ETP_REGISTER_WRITE 0x11
#define ETP_REGISTER_READWRITE 0x00
/*
* Hardware version 2 custom PS/2 command value
*/
#define ETP_PS2_CUSTOM_COMMAND 0xf8
/*
* Times to retry a ps2_command and millisecond delay between tries
*/
#define ETP_PS2_COMMAND_TRIES 3
#define ETP_PS2_COMMAND_DELAY 500
/*
* Times to try to read back a register and millisecond delay between tries
*/
#define ETP_READ_BACK_TRIES 5
#define ETP_READ_BACK_DELAY 2000
/*
* Register bitmasks for hardware version 1
*/
#define ETP_R10_ABSOLUTE_MODE 0x04
#define ETP_R11_4_BYTE_MODE 0x02
/*
* Capability bitmasks
*/
#define ETP_CAP_HAS_ROCKER 0x04
/*
* One hard to find application note states that X axis range is 0 to 576
* and Y axis range is 0 to 384 for harware version 1.
* Edge fuzz might be necessary because of bezel around the touchpad
*/
#define ETP_EDGE_FUZZ_V1 32
#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1)
#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1)
#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1)
#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1)
/*
* The resolution for older v2 hardware doubled.
* (newer v2's firmware provides command so we can query)
*/
#define ETP_XMIN_V2 0
#define ETP_XMAX_V2 1152
#define ETP_YMIN_V2 0
#define ETP_YMAX_V2 768
#define ETP_PMIN_V2 0
#define ETP_PMAX_V2 255
#define ETP_WMIN_V2 0
#define ETP_WMAX_V2 15
/*
* v3 hardware has 2 kinds of packet types,
* v4 hardware has 3.
*/
#define PACKET_UNKNOWN 0x01
#define PACKET_DEBOUNCE 0x02
#define PACKET_V3_HEAD 0x03
#define PACKET_V3_TAIL 0x04
#define PACKET_V4_HEAD 0x05
#define PACKET_V4_MOTION 0x06
#define PACKET_V4_STATUS 0x07
#define PACKET_TRACKPOINT 0x08
/*
* track up to 5 fingers for v4 hardware
*/
#define ETP_MAX_FINGERS 5
/*
* weight value for v4 hardware
*/
#define ETP_WEIGHT_VALUE 5
/*
* Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
*/
#define ETP_BUS_PS2_ONLY 0
#define ETP_BUS_SMB_ALERT_ONLY 1
#define ETP_BUS_SMB_HST_NTFY_ONLY 2
#define ETP_BUS_PS2_SMB_ALERT 3
#define ETP_BUS_PS2_SMB_HST_NTFY 4
/*
* New ICs are either using SMBus Host Notify or just plain PS2.
*
* ETP_FW_VERSION_QUERY is:
* Byte 1:
* - bit 0..3: IC BODY
* Byte 2:
* - bit 4: HiddenButton
* - bit 5: PS2_SMBUS_NOTIFY
* - bit 6: PS2CRCCheck
*/
#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version) \
((((fw_version) & 0x0f2000) == 0x0f2000) && \
((fw_version) & 0x0000ff) > 0)
/*
* The base position for one finger, v4 hardware
*/
struct finger_pos {
unsigned int x;
unsigned int y;
};
struct elantech_device_info {
unsigned char capabilities[3];
unsigned char samples[3];
unsigned char debug;
unsigned char hw_version;
unsigned char pattern;
unsigned int fw_version;
unsigned int ic_version;
unsigned int product_id;
unsigned int x_min;
unsigned int y_min;
unsigned int x_max;
unsigned int y_max;
unsigned int x_res;
unsigned int y_res;
unsigned int x_traces;
unsigned int y_traces;
unsigned int width;
unsigned int bus;
bool paritycheck;
bool jumpy_cursor;
bool reports_pressure;
bool crc_enabled;
bool set_hw_resolution;
bool has_trackpoint;
bool has_middle_button;
int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
unsigned char *param);
};
struct elantech_data {
struct input_dev *tp_dev; /* Relative device for trackpoint */
char tp_phys[32];
unsigned char reg_07;
unsigned char reg_10;
unsigned char reg_11;
unsigned char reg_20;
unsigned char reg_21;
unsigned char reg_22;
unsigned char reg_23;
unsigned char reg_24;
unsigned char reg_25;
unsigned char reg_26;
unsigned int single_finger_reports;
unsigned int y_max;
unsigned int width;
struct finger_pos mt[ETP_MAX_FINGERS];
unsigned char parity[256];
struct elantech_device_info info;
void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
};
int elantech_detect(struct psmouse *psmouse, bool set_properties);
int elantech_init_ps2(struct psmouse *psmouse);
#ifdef CONFIG_MOUSE_PS2_ELANTECH
int elantech_init(struct psmouse *psmouse);
#else
static inline int elantech_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_ELANTECH */
int elantech_init_smbus(struct psmouse *psmouse);
#endif
+455
View File
@@ -0,0 +1,455 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Focaltech TouchPad PS/2 mouse driver
*
* Copyright (c) 2014 Red Hat Inc.
* Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
*
* Red Hat authors:
*
* Hans de Goede <hdegoede@redhat.com>
*/
#include <linux/device.h>
#include <linux/libps2.h>
#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "focaltech.h"
static const char * const focaltech_pnp_ids[] = {
"FLT0101",
"FLT0102",
"FLT0103",
NULL
};
/*
* Even if the kernel is built without support for Focaltech PS/2 touchpads (or
* when the real driver fails to recognize the device), we still have to detect
* them in order to avoid further detection attempts confusing the touchpad.
* This way it at least works in PS/2 mouse compatibility mode.
*/
int focaltech_detect(struct psmouse *psmouse, bool set_properties)
{
if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
return -ENODEV;
if (set_properties) {
psmouse->vendor = "FocalTech";
psmouse->name = "Touchpad";
}
return 0;
}
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
/*
* Packet types - the numbers are not consecutive, so we might be missing
* something here.
*/
#define FOC_TOUCH 0x3 /* bitmap of active fingers */
#define FOC_ABS 0x6 /* absolute position of one finger */
#define FOC_REL 0x9 /* relative position of 1-2 fingers */
#define FOC_MAX_FINGERS 5
/*
* Current state of a single finger on the touchpad.
*/
struct focaltech_finger_state {
/* The touchpad has generated a touch event for the finger */
bool active;
/*
* The touchpad has sent position data for the finger. The
* flag is 0 when the finger is not active, and there is a
* time between the first touch event for the finger and the
* following absolute position packet for the finger where the
* touchpad has declared the finger to be valid, but we do not
* have any valid position yet.
*/
bool valid;
/*
* Absolute position (from the bottom left corner) of the
* finger.
*/
unsigned int x;
unsigned int y;
};
/*
* Description of the current state of the touchpad hardware.
*/
struct focaltech_hw_state {
/*
* The touchpad tracks the positions of the fingers for us,
* the array indices correspond to the finger indices returned
* in the report packages.
*/
struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
/*
* Finger width 0-7 and 15 for a very big contact area.
* 15 value stays until the finger is released.
* Width is reported only in absolute packets.
* Since hardware reports width only for last touching finger,
* there is no need to store width for every specific finger,
* so we keep only last value reported.
*/
unsigned int width;
/* True if the clickpad has been pressed. */
bool pressed;
};
struct focaltech_data {
unsigned int x_max, y_max;
struct focaltech_hw_state state;
};
static void focaltech_report_state(struct psmouse *psmouse)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
struct input_dev *dev = psmouse->dev;
int i;
for (i = 0; i < FOC_MAX_FINGERS; i++) {
struct focaltech_finger_state *finger = &state->fingers[i];
bool active = finger->active && finger->valid;
input_mt_slot(dev, i);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
if (active) {
unsigned int clamped_x, clamped_y;
/*
* The touchpad might report invalid data, so we clamp
* the resulting values so that we do not confuse
* userspace.
*/
clamped_x = clamp(finger->x, 0U, priv->x_max);
clamped_y = clamp(finger->y, 0U, priv->y_max);
input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
input_report_abs(dev, ABS_MT_POSITION_Y,
priv->y_max - clamped_y);
input_report_abs(dev, ABS_TOOL_WIDTH, state->width);
}
}
input_mt_report_pointer_emulation(dev, true);
input_report_key(dev, BTN_LEFT, state->pressed);
input_sync(dev);
}
static void focaltech_process_touch_packet(struct psmouse *psmouse,
unsigned char *packet)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
unsigned char fingers = packet[1];
int i;
state->pressed = (packet[0] >> 4) & 1;
/* the second byte contains a bitmap of all fingers touching the pad */
for (i = 0; i < FOC_MAX_FINGERS; i++) {
state->fingers[i].active = fingers & 0x1;
if (!state->fingers[i].active) {
/*
* Even when the finger becomes active again, we still
* will have to wait for the first valid position.
*/
state->fingers[i].valid = false;
}
fingers >>= 1;
}
}
static void focaltech_process_abs_packet(struct psmouse *psmouse,
unsigned char *packet)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
unsigned int finger;
finger = (packet[1] >> 4) - 1;
if (finger >= FOC_MAX_FINGERS) {
psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
finger);
return;
}
state->pressed = (packet[0] >> 4) & 1;
state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
state->fingers[finger].y = (packet[3] << 8) | packet[4];
state->width = packet[5] >> 4;
state->fingers[finger].valid = true;
}
static void focaltech_process_rel_packet(struct psmouse *psmouse,
unsigned char *packet)
{
struct focaltech_data *priv = psmouse->private;
struct focaltech_hw_state *state = &priv->state;
unsigned int finger1, finger2;
state->pressed = packet[0] >> 7;
finger1 = ((packet[0] >> 4) & 0x7) - 1;
if (finger1 < FOC_MAX_FINGERS) {
state->fingers[finger1].x += (s8)packet[1];
state->fingers[finger1].y += (s8)packet[2];
} else {
psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
finger1);
}
/*
* If there is an odd number of fingers, the last relative
* packet only contains one finger. In this case, the second
* finger index in the packet is 0 (we subtract 1 in the lines
* above to create array indices, so the finger will overflow
* and be above FOC_MAX_FINGERS).
*/
finger2 = ((packet[3] >> 4) & 0x7) - 1;
if (finger2 < FOC_MAX_FINGERS) {
state->fingers[finger2].x += (s8)packet[4];
state->fingers[finger2].y += (s8)packet[5];
}
}
static void focaltech_process_packet(struct psmouse *psmouse)
{
unsigned char *packet = psmouse->packet;
switch (packet[0] & 0xf) {
case FOC_TOUCH:
focaltech_process_touch_packet(psmouse, packet);
break;
case FOC_ABS:
focaltech_process_abs_packet(psmouse, packet);
break;
case FOC_REL:
focaltech_process_rel_packet(psmouse, packet);
break;
default:
psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
break;
}
focaltech_report_state(psmouse);
}
static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
{
if (psmouse->pktcnt >= 6) { /* Full packet received */
focaltech_process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
}
/*
* We might want to do some validation of the data here, but
* we do not know the protocol well enough
*/
return PSMOUSE_GOOD_DATA;
}
static int focaltech_switch_protocol(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
param[0] = 0;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
param[0] = 1;
if (ps2_command(ps2dev, param, 0x10f8))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
return -EIO;
return 0;
}
static void focaltech_reset(struct psmouse *psmouse)
{
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
psmouse_reset(psmouse);
}
static void focaltech_disconnect(struct psmouse *psmouse)
{
focaltech_reset(psmouse);
kfree(psmouse->private);
psmouse->private = NULL;
}
static int focaltech_reconnect(struct psmouse *psmouse)
{
int error;
focaltech_reset(psmouse);
error = focaltech_switch_protocol(psmouse);
if (error) {
psmouse_err(psmouse, "Unable to initialize the device\n");
return error;
}
return 0;
}
static void focaltech_set_input_params(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct focaltech_data *priv = psmouse->private;
/*
* Undo part of setup done for us by psmouse core since touchpad
* is not a relative device.
*/
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
__clear_bit(BTN_RIGHT, dev->keybit);
__clear_bit(BTN_MIDDLE, dev->keybit);
/*
* Now set up our capabilities.
*/
__set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
}
static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
unsigned char *param)
{
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
return -EIO;
param[0] = 0;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
param[0] = reg;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
return -EIO;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -EIO;
return 0;
}
static int focaltech_read_size(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
struct focaltech_data *priv = psmouse->private;
char param[3];
if (focaltech_read_register(ps2dev, 2, param))
return -EIO;
/* not sure whether this is 100% correct */
priv->x_max = (unsigned char)param[1] * 128;
priv->y_max = (unsigned char)param[2] * 128;
return 0;
}
static void focaltech_set_resolution(struct psmouse *psmouse,
unsigned int resolution)
{
/* not supported yet */
}
static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
{
/* not supported yet */
}
static void focaltech_set_scale(struct psmouse *psmouse,
enum psmouse_scale scale)
{
/* not supported yet */
}
int focaltech_init(struct psmouse *psmouse)
{
struct focaltech_data *priv;
int error;
psmouse->private = priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
focaltech_reset(psmouse);
error = focaltech_read_size(psmouse);
if (error) {
psmouse_err(psmouse,
"Unable to read the size of the touchpad\n");
goto fail;
}
error = focaltech_switch_protocol(psmouse);
if (error) {
psmouse_err(psmouse, "Unable to initialize the device\n");
goto fail;
}
focaltech_set_input_params(psmouse);
psmouse->protocol_handler = focaltech_process_byte;
psmouse->pktsize = 6;
psmouse->disconnect = focaltech_disconnect;
psmouse->reconnect = focaltech_reconnect;
psmouse->cleanup = focaltech_reset;
/* resync is not supported yet */
psmouse->resync_time = 0;
/*
* rate/resolution/scale changes are not supported yet, and
* the generic implementations of these functions seem to
* confuse some touchpads
*/
psmouse->set_resolution = focaltech_set_resolution;
psmouse->set_rate = focaltech_set_rate;
psmouse->set_scale = focaltech_set_scale;
return 0;
fail:
focaltech_reset(psmouse);
kfree(priv);
return error;
}
#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Focaltech TouchPad PS/2 mouse driver
*
* Copyright (c) 2014 Red Hat Inc.
* Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
*
* Red Hat authors:
*
* Hans de Goede <hdegoede@redhat.com>
*/
#ifndef _FOCALTECH_H
#define _FOCALTECH_H
int focaltech_detect(struct psmouse *psmouse, bool set_properties);
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
int focaltech_init(struct psmouse *psmouse);
#else
static inline int focaltech_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif
#endif
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for simulating a mouse on GPIO lines.
*
* Copyright (C) 2007 Atmel Corporation
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio/consumer.h>
#include <linux/property.h>
#include <linux/of.h>
/**
* struct gpio_mouse
* @scan_ms: the scan interval in milliseconds.
* @up: GPIO line for up value.
* @down: GPIO line for down value.
* @left: GPIO line for left value.
* @right: GPIO line for right value.
* @bleft: GPIO line for left button.
* @bmiddle: GPIO line for middle button.
* @bright: GPIO line for right button.
*
* This struct must be added to the platform_device in the board code.
* It is used by the gpio_mouse driver to setup GPIO lines and to
* calculate mouse movement.
*/
struct gpio_mouse {
u32 scan_ms;
struct gpio_desc *up;
struct gpio_desc *down;
struct gpio_desc *left;
struct gpio_desc *right;
struct gpio_desc *bleft;
struct gpio_desc *bmiddle;
struct gpio_desc *bright;
};
/*
* Timer function which is run every scan_ms ms when the device is opened.
* The dev input variable is set to the input_dev pointer.
*/
static void gpio_mouse_scan(struct input_dev *input)
{
struct gpio_mouse *gpio = input_get_drvdata(input);
int x, y;
if (gpio->bleft)
input_report_key(input, BTN_LEFT,
gpiod_get_value(gpio->bleft));
if (gpio->bmiddle)
input_report_key(input, BTN_MIDDLE,
gpiod_get_value(gpio->bmiddle));
if (gpio->bright)
input_report_key(input, BTN_RIGHT,
gpiod_get_value(gpio->bright));
x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left);
y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up);
input_report_rel(input, REL_X, x);
input_report_rel(input, REL_Y, y);
input_sync(input);
}
static int gpio_mouse_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_mouse *gmouse;
struct input_dev *input;
int error;
gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL);
if (!gmouse)
return -ENOMEM;
/* Assign some default scanning time */
error = device_property_read_u32(dev, "scan-interval-ms",
&gmouse->scan_ms);
if (error || gmouse->scan_ms == 0) {
dev_warn(dev, "invalid scan time, set to 50 ms\n");
gmouse->scan_ms = 50;
}
gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN);
if (IS_ERR(gmouse->up))
return PTR_ERR(gmouse->up);
gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN);
if (IS_ERR(gmouse->down))
return PTR_ERR(gmouse->down);
gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN);
if (IS_ERR(gmouse->left))
return PTR_ERR(gmouse->left);
gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN);
if (IS_ERR(gmouse->right))
return PTR_ERR(gmouse->right);
gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN);
if (IS_ERR(gmouse->bleft))
return PTR_ERR(gmouse->bleft);
gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle",
GPIOD_IN);
if (IS_ERR(gmouse->bmiddle))
return PTR_ERR(gmouse->bmiddle);
gmouse->bright = devm_gpiod_get_optional(dev, "button-right",
GPIOD_IN);
if (IS_ERR(gmouse->bright))
return PTR_ERR(gmouse->bright);
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input_set_drvdata(input, gmouse);
input_set_capability(input, EV_REL, REL_X);
input_set_capability(input, EV_REL, REL_Y);
if (gmouse->bleft)
input_set_capability(input, EV_KEY, BTN_LEFT);
if (gmouse->bmiddle)
input_set_capability(input, EV_KEY, BTN_MIDDLE);
if (gmouse->bright)
input_set_capability(input, EV_KEY, BTN_RIGHT);
error = input_setup_polling(input, gpio_mouse_scan);
if (error)
return error;
input_set_poll_interval(input, gmouse->scan_ms);
error = input_register_device(input);
if (error) {
dev_err(dev, "could not register input device\n");
return error;
}
dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n",
gmouse->scan_ms,
gmouse->bleft ? "" : "left ",
gmouse->bmiddle ? "" : "middle ",
gmouse->bright ? "" : "right");
return 0;
}
static const struct of_device_id gpio_mouse_of_match[] = {
{ .compatible = "gpio-mouse", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_mouse_of_match);
static struct platform_driver gpio_mouse_device_driver = {
.probe = gpio_mouse_probe,
.driver = {
.name = "gpio_mouse",
.of_match_table = gpio_mouse_of_match,
}
};
module_platform_driver(gpio_mouse_device_driver);
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("GPIO mouse driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */
+353
View File
@@ -0,0 +1,353 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Fujitsu B-series Lifebook PS/2 TouchScreen driver
*
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de>
*
* TouchScreen detection, absolute mode setting and packet layout is taken from
* Harald Hoyer's description of the device.
*/
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "psmouse.h"
#include "lifebook.h"
struct lifebook_data {
struct input_dev *dev2; /* Relative device */
char phys[32];
};
static bool lifebook_present;
static const char *desired_serio_phys;
static int lifebook_limit_serio3(const struct dmi_system_id *d)
{
desired_serio_phys = "isa0060/serio3";
return 1;
}
static bool lifebook_use_6byte_proto;
static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
{
lifebook_use_6byte_proto = true;
return 1;
}
static const struct dmi_system_id lifebook_dmi_table[] __initconst = {
{
/* FLORA-ie 55mi */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
},
},
{
/* LifeBook B */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B Series"),
},
},
{
/* LifeBook B */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
},
},
{
/* Lifebook B */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
},
},
{
/* Lifebook B-2130 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"),
},
},
{
/* Lifebook B213x/B2150 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
},
},
{
/* Zephyr */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
},
},
{
/* Panasonic CF-18 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
},
.callback = lifebook_limit_serio3,
},
{
/* Panasonic CF-28 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
},
.callback = lifebook_set_6byte_proto,
},
{
/* Panasonic CF-29 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
},
.callback = lifebook_set_6byte_proto,
},
{
/* Panasonic CF-72 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
},
.callback = lifebook_set_6byte_proto,
},
{
/* Lifebook B142 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
},
},
{ }
};
void __init lifebook_module_init(void)
{
lifebook_present = dmi_check_system(lifebook_dmi_table);
}
static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
{
struct lifebook_data *priv = psmouse->private;
struct input_dev *dev1 = psmouse->dev;
struct input_dev *dev2 = priv ? priv->dev2 : NULL;
u8 *packet = psmouse->packet;
bool relative_packet = packet[0] & 0x08;
if (relative_packet || !lifebook_use_6byte_proto) {
if (psmouse->pktcnt != 3)
return PSMOUSE_GOOD_DATA;
} else {
switch (psmouse->pktcnt) {
case 1:
return (packet[0] & 0xf8) == 0x00 ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
case 2:
return PSMOUSE_GOOD_DATA;
case 3:
return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
case 4:
return (packet[3] & 0xf8) == 0xc0 ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
case 5:
return (packet[4] & 0xc0) == (packet[2] & 0xc0) ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
case 6:
if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0))
return PSMOUSE_BAD_DATA;
if ((packet[5] & 0xc0) != (packet[1] & 0xc0))
return PSMOUSE_BAD_DATA;
break; /* report data */
}
}
if (relative_packet) {
if (!dev2)
psmouse_warn(psmouse,
"got relative packet but no relative device set up\n");
} else {
if (lifebook_use_6byte_proto) {
input_report_abs(dev1, ABS_X,
((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f));
input_report_abs(dev1, ABS_Y,
4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f)));
} else {
input_report_abs(dev1, ABS_X,
(packet[1] | ((packet[0] & 0x30) << 4)));
input_report_abs(dev1, ABS_Y,
1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
}
input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04);
input_sync(dev1);
}
if (dev2) {
if (relative_packet)
psmouse_report_standard_motion(dev2, packet);
psmouse_report_standard_buttons(dev2, packet[0]);
input_sync(dev2);
}
return PSMOUSE_FULL_PACKET;
}
static int lifebook_absolute_mode(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param;
int error;
error = psmouse_reset(psmouse);
if (error)
return error;
/*
* Enable absolute output -- ps2_command fails always but if
* you leave this call out the touchscreen will never send
* absolute coordinates
*/
param = lifebook_use_6byte_proto ? 0x08 : 0x07;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
return 0;
}
static void lifebook_relative_mode(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param = 0x06;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
}
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
static const u8 params[] = { 0, 1, 2, 2, 3 };
u8 p;
if (resolution == 0 || resolution > 400)
resolution = 400;
p = params[resolution / 100];
ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
psmouse->resolution = 50 << p;
}
static void lifebook_disconnect(struct psmouse *psmouse)
{
struct lifebook_data *priv = psmouse->private;
psmouse_reset(psmouse);
if (priv) {
input_unregister_device(priv->dev2);
kfree(priv);
}
psmouse->private = NULL;
}
int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
if (!lifebook_present)
return -ENXIO;
if (desired_serio_phys &&
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
return -ENXIO;
if (set_properties) {
psmouse->vendor = "Fujitsu";
psmouse->name = "Lifebook TouchScreen";
}
return 0;
}
static int lifebook_create_relative_device(struct psmouse *psmouse)
{
struct input_dev *dev2;
struct lifebook_data *priv;
int error = -ENOMEM;
priv = kzalloc_obj(*priv);
dev2 = input_allocate_device();
if (!priv || !dev2)
goto err_out;
priv->dev2 = dev2;
scnprintf(priv->phys, sizeof(priv->phys),
"%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
dev2->name = "LBPS/2 Fujitsu Lifebook Touchpad";
dev2->id.bustype = BUS_I8042;
dev2->id.vendor = 0x0002;
dev2->id.product = PSMOUSE_LIFEBOOK;
dev2->id.version = 0x0000;
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
input_set_capability(dev2, EV_REL, REL_X);
input_set_capability(dev2, EV_REL, REL_Y);
input_set_capability(dev2, EV_KEY, BTN_LEFT);
input_set_capability(dev2, EV_KEY, BTN_RIGHT);
error = input_register_device(priv->dev2);
if (error)
goto err_out;
psmouse->private = priv;
return 0;
err_out:
input_free_device(dev2);
kfree(priv);
return error;
}
int lifebook_init(struct psmouse *psmouse)
{
struct input_dev *dev1 = psmouse->dev;
int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
int error;
error = lifebook_absolute_mode(psmouse);
if (error)
return error;
/* Clear default capabilities */
bitmap_zero(dev1->evbit, EV_CNT);
bitmap_zero(dev1->relbit, REL_CNT);
bitmap_zero(dev1->keybit, KEY_CNT);
input_set_capability(dev1, EV_KEY, BTN_TOUCH);
input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
if (!desired_serio_phys) {
error = lifebook_create_relative_device(psmouse);
if (error) {
lifebook_relative_mode(psmouse);
return error;
}
}
psmouse->protocol_handler = lifebook_process_byte;
psmouse->set_resolution = lifebook_set_resolution;
psmouse->disconnect = lifebook_disconnect;
psmouse->reconnect = lifebook_absolute_mode;
psmouse->model = lifebook_use_6byte_proto ? 6 : 3;
/*
* Use packet size = 3 even when using 6-byte protocol because
* that's what POLL will return on Lifebooks (according to spec).
*/
psmouse->pktsize = 3;
return 0;
}
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Fujitsu B-series Lifebook PS/2 TouchScreen driver
*
* Copyright (c) 2005 Vojtech Pavlik
*/
#ifndef _LIFEBOOK_H
#define _LIFEBOOK_H
int lifebook_detect(struct psmouse *psmouse, bool set_properties);
int lifebook_init(struct psmouse *psmouse);
#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
void lifebook_module_init(void);
#else
static inline void lifebook_module_init(void)
{
}
#endif
#endif
+444
View File
@@ -0,0 +1,444 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Logitech PS/2++ mouse driver
*
* Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2003 Eric Wong <eric@yhbt.net>
*/
#include <linux/bitops.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/types.h>
#include "psmouse.h"
#include "logips2pp.h"
/* Logitech mouse types */
#define PS2PP_KIND_WHEEL 1
#define PS2PP_KIND_MX 2
#define PS2PP_KIND_TP3 3
#define PS2PP_KIND_TRACKMAN 4
/* Logitech mouse features */
#define PS2PP_WHEEL BIT(0)
#define PS2PP_HWHEEL BIT(1)
#define PS2PP_SIDE_BTN BIT(2)
#define PS2PP_EXTRA_BTN BIT(3)
#define PS2PP_TASK_BTN BIT(4)
#define PS2PP_NAV_BTN BIT(5)
struct ps2pp_info {
u8 model;
u8 kind;
u16 features;
};
/*
* Process a PS2++ or PS2T++ packet.
*/
static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
u8 *packet = psmouse->packet;
if (psmouse->pktcnt < 3)
return PSMOUSE_GOOD_DATA;
/*
* Full packet accumulated, process it
*/
if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
/* Logitech extended packet */
switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
case 0x0d: /* Mouse extra info */
input_report_rel(dev,
packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
-sign_extend32(packet[2], 3));
input_report_key(dev, BTN_SIDE, packet[2] & BIT(4));
input_report_key(dev, BTN_EXTRA, packet[2] & BIT(5));
break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, packet[2] & BIT(0));
input_report_key(dev, BTN_EXTRA, packet[2] & BIT(1));
input_report_key(dev, BTN_TASK, packet[2] & BIT(2));
input_report_key(dev, BTN_BACK, packet[2] & BIT(3));
input_report_key(dev, BTN_FORWARD, packet[2] & BIT(4));
break;
case 0x0f: /* TouchPad extra info */
input_report_rel(dev,
packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
-sign_extend32(packet[2] >> 4, 3));
packet[0] = packet[2] | BIT(3);
break;
default:
psmouse_dbg(psmouse,
"Received PS2++ packet #%x, but don't know how to handle.\n",
(packet[1] >> 4) | (packet[0] & 0x30));
break;
}
psmouse_report_standard_buttons(dev, packet[0]);
} else {
/* Standard PS/2 motion data */
psmouse_report_standard_packet(dev, packet);
}
input_sync(dev);
return PSMOUSE_FULL_PACKET;
}
/*
* ps2pp_cmd() sends a PS2++ command, sliced into two bit
* pieces through the SETRES command. This is needed to send extended
* commands to mice on notebooks that try to understand the PS/2 protocol
* Ugly.
*/
static int ps2pp_cmd(struct psmouse *psmouse, u8 *param, u8 command)
{
int error;
error = ps2_sliced_command(&psmouse->ps2dev, command);
if (error)
return error;
error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300);
if (error)
return error;
return 0;
}
/*
* SmartScroll / CruiseControl for some newer Logitech mice Defaults to
* enabled if we do nothing to it. Of course I put this in because I want it
* disabled :P
* 1 - enabled (if previously disabled, also default)
* 0 - disabled
*/
static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param[4];
ps2pp_cmd(psmouse, param, 0x32);
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
param[0] = smartscroll;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
}
static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse,
void *data, char *buf)
{
return sprintf(buf, "%d\n", psmouse->smartscroll);
}
static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
unsigned int value;
int err;
err = kstrtouint(buf, 10, &value);
if (err)
return err;
if (value > 1)
return -EINVAL;
ps2pp_set_smartscroll(psmouse, value);
psmouse->smartscroll = value;
return count;
}
PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
/*
* Support 800 dpi resolution _only_ if the user wants it (there are good
* reasons to not use it even if the mouse supports it, and of course there are
* also good reasons to use it, let the user decide).
*/
static void ps2pp_set_resolution(struct psmouse *psmouse,
unsigned int resolution)
{
if (resolution > 400) {
struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param = 3;
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
psmouse->resolution = 800;
} else
psmouse_set_resolution(psmouse, resolution);
}
static void ps2pp_disconnect(struct psmouse *psmouse)
{
device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
}
static const struct ps2pp_info *get_model_info(unsigned char model)
{
static const struct ps2pp_info ps2pp_list[] = {
{ 1, 0, 0 }, /* Simple 2-button mouse */
{ 12, 0, PS2PP_SIDE_BTN},
{ 13, 0, 0 },
{ 15, PS2PP_KIND_MX, /* MX1000 */
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
{ 40, 0, PS2PP_SIDE_BTN },
{ 41, 0, PS2PP_SIDE_BTN },
{ 42, 0, PS2PP_SIDE_BTN },
{ 43, 0, PS2PP_SIDE_BTN },
{ 50, 0, 0 },
{ 51, 0, 0 },
{ 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 56, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */
{ 61, PS2PP_KIND_MX, /* MX700 */
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
{ 66, PS2PP_KIND_MX, /* MX3100 receiver */
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
{ 72, PS2PP_KIND_TRACKMAN, 0 }, /* T-CH11: TrackMan Marble */
{ 73, PS2PP_KIND_TRACKMAN, PS2PP_SIDE_BTN }, /* TrackMan FX */
{ 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 79, PS2PP_KIND_TRACKMAN, PS2PP_WHEEL }, /* TrackMan with wheel */
{ 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 85, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 86, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 87, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 96, 0, 0 },
{ 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL },
{ 99, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 100, PS2PP_KIND_MX, /* MX510 */
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
{ 111, PS2PP_KIND_MX, PS2PP_WHEEL | PS2PP_SIDE_BTN }, /* MX300 reports task button as side */
{ 112, PS2PP_KIND_MX, /* MX500 */
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
{ 114, PS2PP_KIND_MX, /* MX310 */
PS2PP_WHEEL | PS2PP_SIDE_BTN |
PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }
};
int i;
for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++)
if (model == ps2pp_list[i].model)
return &ps2pp_list[i];
return NULL;
}
/*
* Set up input device's properties based on the detected mouse model.
*/
static void ps2pp_set_model_properties(struct psmouse *psmouse,
const struct ps2pp_info *model_info,
bool using_ps2pp)
{
struct input_dev *input_dev = psmouse->dev;
if (model_info->features & PS2PP_SIDE_BTN)
input_set_capability(input_dev, EV_KEY, BTN_SIDE);
if (model_info->features & PS2PP_EXTRA_BTN)
input_set_capability(input_dev, EV_KEY, BTN_EXTRA);
if (model_info->features & PS2PP_TASK_BTN)
input_set_capability(input_dev, EV_KEY, BTN_TASK);
if (model_info->features & PS2PP_NAV_BTN) {
input_set_capability(input_dev, EV_KEY, BTN_FORWARD);
input_set_capability(input_dev, EV_KEY, BTN_BACK);
}
if (model_info->features & PS2PP_WHEEL)
input_set_capability(input_dev, EV_REL, REL_WHEEL);
if (model_info->features & PS2PP_HWHEEL)
input_set_capability(input_dev, EV_REL, REL_HWHEEL);
switch (model_info->kind) {
case PS2PP_KIND_WHEEL:
psmouse->name = "Wheel Mouse";
break;
case PS2PP_KIND_MX:
psmouse->name = "MX Mouse";
break;
case PS2PP_KIND_TP3:
psmouse->name = "TouchPad 3";
break;
case PS2PP_KIND_TRACKMAN:
psmouse->name = "TrackMan";
break;
default:
/*
* Set name to "Mouse" only when using PS2++,
* otherwise let other protocols define suitable
* name
*/
if (using_ps2pp)
psmouse->name = "Mouse";
break;
}
}
static int ps2pp_setup_protocol(struct psmouse *psmouse,
const struct ps2pp_info *model_info)
{
int error;
psmouse->protocol_handler = ps2pp_process_byte;
psmouse->pktsize = 3;
if (model_info->kind != PS2PP_KIND_TP3) {
psmouse->set_resolution = ps2pp_set_resolution;
psmouse->disconnect = ps2pp_disconnect;
error = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_smartscroll.dattr);
if (error) {
psmouse_err(psmouse,
"failed to create smartscroll sysfs attribute, error: %d\n",
error);
return error;
}
}
return 0;
}
/*
* Logitech magic init. Detect whether the mouse is a Logitech one
* and its exact model and try turning on extended protocol for ones
* that support it.
*/
int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
const struct ps2pp_info *model_info;
u8 param[4];
u8 model, buttons;
bool use_ps2pp = false;
int error;
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
param[1] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
buttons = param[1];
if (!model || !buttons)
return -ENXIO;
model_info = get_model_info(model);
if (model_info) {
/*
* Do Logitech PS2++ / PS2T++ magic init.
*/
if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */
/* Unprotect RAM */
param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
ps2_command(ps2dev, param, 0x30d1);
/* Enable features */
param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
ps2_command(ps2dev, param, 0x30d1);
/* Enable PS2++ */
param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
ps2_command(ps2dev, param, 0x30d1);
param[0] = 0;
if (!ps2_command(ps2dev, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 &&
param[2] == 0x14) {
use_ps2pp = true;
}
} else {
param[0] = param[1] = param[2] = 0;
ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
ps2pp_cmd(psmouse, param, 0xDB);
if ((param[0] & 0x78) == 0x48 &&
(param[1] & 0xf3) == 0xc2 &&
(param[2] & 0x03) == ((param[1] >> 2) & 3)) {
ps2pp_set_smartscroll(psmouse, false);
use_ps2pp = true;
}
}
} else {
psmouse_warn(psmouse,
"Detected unknown Logitech mouse model %d\n",
model);
}
if (set_properties) {
psmouse->vendor = "Logitech";
psmouse->model = model;
if (use_ps2pp) {
error = ps2pp_setup_protocol(psmouse, model_info);
if (error)
return error;
}
if (buttons >= 3)
input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
}
return use_ps2pp ? 0 : -ENXIO;
}
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Logitech PS/2++ mouse driver header
*
* Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz>
*/
#ifndef _LOGIPS2PP_H
#define _LOGIPS2PP_H
int ps2pp_detect(struct psmouse *psmouse, bool set_properties);
#endif
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SEGA Dreamcast mouse driver
* Based on drivers/usb/usbmouse.c
*
* Copyright (c) Yaegashi Takeshi, 2001
* Copyright (c) Adrian McMenamin, 2008 - 2009
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/maple.h>
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
MODULE_LICENSE("GPL");
struct dc_mouse {
struct input_dev *dev;
struct maple_device *mdev;
};
static void dc_mouse_callback(struct mapleq *mq)
{
int buttons, relx, rely, relz;
struct maple_device *mapledev = mq->dev;
struct dc_mouse *mse = maple_get_drvdata(mapledev);
struct input_dev *dev = mse->dev;
unsigned char *res = mq->recvbuf->buf;
buttons = ~res[8];
relx = *(unsigned short *)(res + 12) - 512;
rely = *(unsigned short *)(res + 14) - 512;
relz = *(unsigned short *)(res + 16) - 512;
input_report_key(dev, BTN_LEFT, buttons & 4);
input_report_key(dev, BTN_MIDDLE, buttons & 9);
input_report_key(dev, BTN_RIGHT, buttons & 2);
input_report_rel(dev, REL_X, relx);
input_report_rel(dev, REL_Y, rely);
input_report_rel(dev, REL_WHEEL, relz);
input_sync(dev);
}
static int dc_mouse_open(struct input_dev *dev)
{
struct dc_mouse *mse = input_get_drvdata(dev);
maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
MAPLE_FUNC_MOUSE);
return 0;
}
static void dc_mouse_close(struct input_dev *dev)
{
struct dc_mouse *mse = input_get_drvdata(dev);
maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
MAPLE_FUNC_MOUSE);
}
/* allow the mouse to be used */
static int probe_maple_mouse(struct device *dev)
{
struct maple_device *mdev = to_maple_dev(dev);
struct maple_driver *mdrv = to_maple_driver(dev->driver);
int error;
struct input_dev *input_dev;
struct dc_mouse *mse;
mse = kzalloc_obj(*mse);
if (!mse) {
error = -ENOMEM;
goto fail;
}
input_dev = input_allocate_device();
if (!input_dev) {
error = -ENOMEM;
goto fail_nomem;
}
mse->dev = input_dev;
mse->mdev = mdev;
maple_set_drvdata(mdev, mse);
input_set_drvdata(input_dev, mse);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
BIT_MASK(REL_WHEEL);
input_dev->open = dc_mouse_open;
input_dev->close = dc_mouse_close;
input_dev->name = mdev->product_name;
input_dev->id.bustype = BUS_HOST;
error = input_register_device(input_dev);
if (error)
goto fail_register;
mdev->driver = mdrv;
return error;
fail_register:
input_free_device(input_dev);
maple_set_drvdata(mdev, NULL);
fail_nomem:
kfree(mse);
fail:
return error;
}
static int remove_maple_mouse(struct device *dev)
{
struct maple_device *mdev = to_maple_dev(dev);
struct dc_mouse *mse = maple_get_drvdata(mdev);
mdev->callback = NULL;
input_unregister_device(mse->dev);
maple_set_drvdata(mdev, NULL);
kfree(mse);
return 0;
}
static struct maple_driver dc_mouse_driver = {
.function = MAPLE_FUNC_MOUSE,
.drv = {
.name = "Dreamcast_mouse",
.probe = probe_maple_mouse,
.remove = remove_maple_mouse,
},
};
static int __init dc_mouse_init(void)
{
return maple_driver_register(&dc_mouse_driver);
}
static void __exit dc_mouse_exit(void)
{
maple_driver_unregister(&dc_mouse_driver);
}
module_init(dc_mouse_init);
module_exit(dc_mouse_exit);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,320 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 Red Hat, Inc
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/libps2.h>
#include <linux/i2c.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "psmouse.h"
struct psmouse_smbus_dev {
struct i2c_board_info board;
struct psmouse *psmouse;
struct i2c_client *client;
struct list_head node;
bool dead;
bool need_deactivate;
};
static LIST_HEAD(psmouse_smbus_list);
static DEFINE_MUTEX(psmouse_smbus_mutex);
static struct workqueue_struct *psmouse_smbus_wq;
static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
{
struct psmouse_smbus_dev *smbdev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
return;
guard(mutex)(&psmouse_smbus_mutex);
list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
if (smbdev->dead)
continue;
if (smbdev->client)
continue;
/*
* Here would be a good place to check if device is actually
* present, but it seems that SMBus will not respond unless we
* fully reset PS/2 connection. So cross our fingers, and try
* to switch over, hopefully our system will not have too many
* "host notify" I2C adapters.
*/
psmouse_dbg(smbdev->psmouse,
"SMBus candidate adapter appeared, triggering rescan\n");
serio_rescan(smbdev->psmouse->ps2dev.serio);
}
}
static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
{
struct psmouse_smbus_dev *smbdev, *tmp;
guard(mutex)(&psmouse_smbus_mutex);
list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
if (smbdev->client != client)
continue;
kfree(client->dev.platform_data);
client->dev.platform_data = NULL;
if (!smbdev->dead) {
psmouse_dbg(smbdev->psmouse,
"Marking SMBus companion %s as gone\n",
dev_name(&smbdev->client->dev));
smbdev->dead = true;
device_link_remove(&smbdev->client->dev,
&smbdev->psmouse->ps2dev.serio->dev);
serio_rescan(smbdev->psmouse->ps2dev.serio);
} else {
list_del(&smbdev->node);
kfree(smbdev);
}
}
}
static int psmouse_smbus_notifier_call(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
if (dev->type == &i2c_adapter_type)
psmouse_smbus_check_adapter(to_i2c_adapter(dev));
break;
case BUS_NOTIFY_REMOVED_DEVICE:
if (dev->type == &i2c_client_type)
psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
break;
}
return 0;
}
static struct notifier_block psmouse_smbus_notifier = {
.notifier_call = psmouse_smbus_notifier_call,
};
static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
{
return PSMOUSE_FULL_PACKET;
}
static int psmouse_smbus_reconnect(struct psmouse *psmouse)
{
struct psmouse_smbus_dev *smbdev = psmouse->private;
if (smbdev->need_deactivate)
psmouse_deactivate(psmouse);
return 0;
}
struct psmouse_smbus_removal_work {
struct work_struct work;
struct i2c_client *client;
};
static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
{
struct psmouse_smbus_removal_work *rwork =
container_of(work, struct psmouse_smbus_removal_work, work);
dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
i2c_unregister_device(rwork->client);
kfree(rwork);
}
/*
* This schedules removal of SMBus companion device. We have to do
* it in a separate tread to avoid deadlocking on psmouse_mutex in
* case the device has a trackstick (which is also driven by psmouse).
*
* Note that this may be racing with i2c adapter removal, but we
* can't do anything about that: i2c automatically destroys clients
* attached to an adapter that is being removed. This has to be
* fixed in i2c core.
*/
static void psmouse_smbus_schedule_remove(struct i2c_client *client)
{
struct psmouse_smbus_removal_work *rwork;
rwork = kzalloc_obj(*rwork);
if (rwork) {
INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
rwork->client = client;
queue_work(psmouse_smbus_wq, &rwork->work);
}
}
static void psmouse_smbus_disconnect(struct psmouse *psmouse)
{
struct psmouse_smbus_dev *smbdev = psmouse->private;
guard(mutex)(&psmouse_smbus_mutex);
if (smbdev->dead) {
list_del(&smbdev->node);
kfree(smbdev);
} else {
smbdev->dead = true;
device_link_remove(&smbdev->client->dev,
&psmouse->ps2dev.serio->dev);
psmouse_dbg(smbdev->psmouse,
"posting removal request for SMBus companion %s\n",
dev_name(&smbdev->client->dev));
psmouse_smbus_schedule_remove(smbdev->client);
}
psmouse->private = NULL;
}
static int psmouse_smbus_create_companion(struct device *dev, void *data)
{
struct psmouse_smbus_dev *smbdev = data;
unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
struct i2c_adapter *adapter;
struct i2c_client *client;
adapter = i2c_verify_adapter(dev);
if (!adapter)
return 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
return 0;
client = i2c_new_scanned_device(adapter, &smbdev->board,
addr_list, NULL);
if (IS_ERR(client))
return 0;
/* We have our(?) device, stop iterating i2c bus. */
smbdev->client = client;
return 1;
}
void psmouse_smbus_cleanup(struct psmouse *psmouse)
{
struct psmouse_smbus_dev *smbdev, *tmp;
guard(mutex)(&psmouse_smbus_mutex);
list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
if (psmouse == smbdev->psmouse) {
list_del(&smbdev->node);
kfree(smbdev);
}
}
}
int psmouse_smbus_init(struct psmouse *psmouse,
const struct i2c_board_info *board,
const void *pdata, size_t pdata_size,
bool need_deactivate,
bool leave_breadcrumbs)
{
struct psmouse_smbus_dev *smbdev;
int error;
smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
if (!smbdev)
return -ENOMEM;
smbdev->psmouse = psmouse;
smbdev->board = *board;
smbdev->need_deactivate = need_deactivate;
if (pdata) {
smbdev->board.platform_data = kmemdup(pdata, pdata_size,
GFP_KERNEL);
if (!smbdev->board.platform_data) {
kfree(smbdev);
return -ENOMEM;
}
}
if (need_deactivate)
psmouse_deactivate(psmouse);
psmouse->private = smbdev;
psmouse->protocol_handler = psmouse_smbus_process_byte;
psmouse->reconnect = psmouse_smbus_reconnect;
psmouse->fast_reconnect = psmouse_smbus_reconnect;
psmouse->disconnect = psmouse_smbus_disconnect;
psmouse->resync_time = 0;
scoped_guard(mutex, &psmouse_smbus_mutex) {
list_add_tail(&smbdev->node, &psmouse_smbus_list);
}
/* Bind to already existing adapters right away */
error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
if (smbdev->client) {
/* We have our companion device */
if (!device_link_add(&smbdev->client->dev,
&psmouse->ps2dev.serio->dev,
DL_FLAG_STATELESS))
psmouse_warn(psmouse,
"failed to set up link with iSMBus companion %s\n",
dev_name(&smbdev->client->dev));
return 0;
}
/*
* If we did not create i2c device we will not need platform
* data even if we are leaving breadcrumbs.
*/
kfree(smbdev->board.platform_data);
smbdev->board.platform_data = NULL;
if (error < 0 || !leave_breadcrumbs) {
scoped_guard(mutex, &psmouse_smbus_mutex) {
list_del(&smbdev->node);
}
kfree(smbdev);
}
return error < 0 ? error : -EAGAIN;
}
int __init psmouse_smbus_module_init(void)
{
int error;
psmouse_smbus_wq = alloc_workqueue("psmouse-smbus", WQ_UNBOUND, 0);
if (!psmouse_smbus_wq)
return -ENOMEM;
error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
if (error) {
pr_err("failed to register i2c bus notifier: %d\n", error);
destroy_workqueue(psmouse_smbus_wq);
return error;
}
return 0;
}
void psmouse_smbus_module_exit(void)
{
bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
destroy_workqueue(psmouse_smbus_wq);
}
+249
View File
@@ -0,0 +1,249 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PSMOUSE_H
#define _PSMOUSE_H
#define PSMOUSE_OOB_NONE 0x00
#define PSMOUSE_OOB_EXTRA_BTNS 0x01
#define PSMOUSE_CMD_SETSCALE11 0x00e6
#define PSMOUSE_CMD_SETSCALE21 0x00e7
#define PSMOUSE_CMD_SETRES 0x10e8
#define PSMOUSE_CMD_GETINFO 0x03e9
#define PSMOUSE_CMD_SETSTREAM 0x00ea
#define PSMOUSE_CMD_SETPOLL 0x00f0
#define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */
#define PSMOUSE_CMD_RESET_WRAP 0x00ec
#define PSMOUSE_CMD_GETID 0x02f2
#define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_DISABLE 0x00f5
#define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
#define PSMOUSE_RET_BAT 0xaa
#define PSMOUSE_RET_ID 0x00
#define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe
enum psmouse_state {
PSMOUSE_IGNORE,
PSMOUSE_INITIALIZING,
PSMOUSE_RESYNCING,
PSMOUSE_CMD_MODE,
PSMOUSE_ACTIVATED,
};
/* psmouse protocol handler return codes */
typedef enum {
PSMOUSE_BAD_DATA,
PSMOUSE_GOOD_DATA,
PSMOUSE_FULL_PACKET
} psmouse_ret_t;
enum psmouse_scale {
PSMOUSE_SCALE11,
PSMOUSE_SCALE21
};
enum psmouse_type {
PSMOUSE_NONE,
PSMOUSE_PS2,
PSMOUSE_PS2PP,
PSMOUSE_THINKPS,
PSMOUSE_GENPS,
PSMOUSE_IMPS,
PSMOUSE_IMEX,
PSMOUSE_SYNAPTICS,
PSMOUSE_ALPS,
PSMOUSE_LIFEBOOK,
PSMOUSE_TRACKPOINT,
PSMOUSE_TOUCHKIT_PS2,
PSMOUSE_CORTRON,
PSMOUSE_HGPK, /* No longer used */
PSMOUSE_ELANTECH,
PSMOUSE_FSP,
PSMOUSE_SYNAPTICS_RELATIVE,
PSMOUSE_CYPRESS,
PSMOUSE_FOCALTECH,
PSMOUSE_VMMOUSE,
PSMOUSE_BYD,
PSMOUSE_SYNAPTICS_SMBUS,
PSMOUSE_ELANTECH_SMBUS,
PSMOUSE_AUTO /* This one should always be last */
};
struct psmouse;
struct psmouse_protocol {
enum psmouse_type type;
bool maxproto;
bool ignore_parity; /* Protocol should ignore parity errors from KBC */
bool try_passthru; /* Try protocol also on passthrough ports */
bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */
const char *name;
const char *alias;
int (*detect)(struct psmouse *, bool);
int (*init)(struct psmouse *);
};
struct psmouse {
void *private;
struct input_dev *dev;
struct ps2dev ps2dev;
struct work_struct resync_work;
const char *vendor;
const char *name;
const struct psmouse_protocol *protocol;
unsigned char packet[8];
unsigned char badbyte;
unsigned char pktcnt;
unsigned char pktsize;
unsigned char oob_data_type;
unsigned char extra_buttons;
bool acks_disable_command;
unsigned int model;
unsigned long last;
unsigned long out_of_sync_cnt;
unsigned long num_resyncs;
enum psmouse_state state;
char devname[64];
char phys[32];
unsigned int rate;
unsigned int resolution;
unsigned int resetafter;
unsigned int resync_time;
bool smartscroll; /* Logitech only */
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse);
void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution);
void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
int (*reconnect)(struct psmouse *psmouse);
int (*fast_reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
void (*cleanup)(struct psmouse *psmouse);
int (*poll)(struct psmouse *psmouse);
void (*pt_activate)(struct psmouse *psmouse);
void (*pt_deactivate)(struct psmouse *psmouse);
};
struct psmouse *psmouse_from_serio(struct serio *serio);
int psmouse_reset(struct psmouse *psmouse);
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse);
int psmouse_activate(struct psmouse *psmouse);
int psmouse_deactivate(struct psmouse *psmouse);
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]);
void psmouse_report_standard_buttons(struct input_dev *, u8 buttons);
void psmouse_report_standard_motion(struct input_dev *, u8 *packet);
void psmouse_report_standard_packet(struct input_dev *, u8 *packet);
struct psmouse_attribute {
struct device_attribute dattr;
void *data;
ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf);
ssize_t (*set)(struct psmouse *psmouse, void *data,
const char *buf, size_t count);
bool protect;
};
#define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr)
ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
#define __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) \
static struct psmouse_attribute psmouse_attr_##_name = { \
.dattr = { \
.attr = { \
.name = __stringify(_name), \
.mode = _mode, \
}, \
.show = psmouse_attr_show_helper, \
.store = psmouse_attr_set_helper, \
}, \
.data = _data, \
.show = _show, \
.set = _set, \
.protect = _protect, \
}
#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \
static ssize_t _show(struct psmouse *, void *, char *); \
static ssize_t _set(struct psmouse *, void *, const char *, size_t); \
__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect)
#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \
__PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, true)
#define PSMOUSE_DEFINE_RO_ATTR(_name, _mode, _data, _show) \
static ssize_t _show(struct psmouse *, void *, char *); \
__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, NULL, true)
#define PSMOUSE_DEFINE_WO_ATTR(_name, _mode, _data, _set) \
static ssize_t _set(struct psmouse *, void *, const char *, size_t); \
__PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true)
#ifndef psmouse_fmt
#define psmouse_fmt(fmt) KBUILD_BASENAME ": " fmt
#endif
#define psmouse_dbg(psmouse, format, ...) \
dev_dbg(&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
#define psmouse_info(psmouse, format, ...) \
dev_info(&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
#define psmouse_warn(psmouse, format, ...) \
dev_warn(&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
#define psmouse_err(psmouse, format, ...) \
dev_err(&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
#define psmouse_notice(psmouse, format, ...) \
dev_notice(&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
#define psmouse_printk(level, psmouse, format, ...) \
dev_printk(level, \
&(psmouse)->ps2dev.serio->dev, \
psmouse_fmt(format), ##__VA_ARGS__)
#ifdef CONFIG_MOUSE_PS2_SMBUS
int psmouse_smbus_module_init(void);
void psmouse_smbus_module_exit(void);
struct i2c_board_info;
int psmouse_smbus_init(struct psmouse *psmouse,
const struct i2c_board_info *board,
const void *pdata, size_t pdata_size,
bool need_deactivate,
bool leave_breadcrumbs);
void psmouse_smbus_cleanup(struct psmouse *psmouse);
#else /* !CONFIG_MOUSE_PS2_SMBUS */
static inline int psmouse_smbus_module_init(void)
{
return 0;
}
static inline void psmouse_smbus_module_exit(void)
{
}
static inline void psmouse_smbus_cleanup(struct psmouse *psmouse)
{
}
#endif /* CONFIG_MOUSE_PS2_SMBUS */
#endif /* _PSMOUSE_H */
+113
View File
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Acorn RiscPC mouse driver for Linux/ARM
*
* Copyright (c) 2000-2002 Vojtech Pavlik
* Copyright (C) 1996-2002 Russell King
*/
/*
*
* This handles the Acorn RiscPCs mouse. We basically have a couple of
* hardware registers that track the sensor count for the X-Y movement and
* another register holding the button state. On every VSYNC interrupt we read
* the complete state and then work out if something has changed.
*/
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/hardware/iomd.h>
MODULE_AUTHOR("Vojtech Pavlik, Russell King");
MODULE_DESCRIPTION("Acorn RiscPC mouse driver");
MODULE_LICENSE("GPL");
static short rpcmouse_lastx, rpcmouse_lasty;
static struct input_dev *rpcmouse_dev;
static irqreturn_t rpcmouse_irq(int irq, void *dev_id)
{
struct input_dev *dev = dev_id;
short x, y, dx, dy, b;
x = (short) iomd_readl(IOMD_MOUSEX);
y = (short) iomd_readl(IOMD_MOUSEY);
b = (short) (__raw_readl(IOMEM(0xe0310000)) ^ 0x70);
dx = x - rpcmouse_lastx;
dy = y - rpcmouse_lasty;
rpcmouse_lastx = x;
rpcmouse_lasty = y;
input_report_rel(dev, REL_X, dx);
input_report_rel(dev, REL_Y, -dy);
input_report_key(dev, BTN_LEFT, b & 0x40);
input_report_key(dev, BTN_MIDDLE, b & 0x20);
input_report_key(dev, BTN_RIGHT, b & 0x10);
input_sync(dev);
return IRQ_HANDLED;
}
static int __init rpcmouse_init(void)
{
int err;
rpcmouse_dev = input_allocate_device();
if (!rpcmouse_dev)
return -ENOMEM;
rpcmouse_dev->name = "Acorn RiscPC Mouse";
rpcmouse_dev->phys = "rpcmouse/input0";
rpcmouse_dev->id.bustype = BUS_HOST;
rpcmouse_dev->id.vendor = 0x0005;
rpcmouse_dev->id.product = 0x0001;
rpcmouse_dev->id.version = 0x0100;
rpcmouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
rpcmouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
rpcmouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX);
rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY);
if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, IRQF_SHARED, "rpcmouse", rpcmouse_dev)) {
printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
err = -EBUSY;
goto err_free_dev;
}
err = input_register_device(rpcmouse_dev);
if (err)
goto err_free_irq;
return 0;
err_free_irq:
free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
err_free_dev:
input_free_device(rpcmouse_dev);
return err;
}
static void __exit rpcmouse_exit(void)
{
free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
input_unregister_device(rpcmouse_dev);
}
module_init(rpcmouse_init);
module_exit(rpcmouse_exit);
File diff suppressed because it is too large Load Diff
+114
View File
@@ -0,0 +1,114 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*-
* Finger Sensing Pad PS/2 mouse driver.
*
* Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
* Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
*/
#ifndef __SENTELIC_H
#define __SENTELIC_H
/* Finger-sensing Pad information registers */
#define FSP_REG_DEVICE_ID 0x00
#define FSP_REG_VERSION 0x01
#define FSP_REG_REVISION 0x04
#define FSP_REG_TMOD_STATUS1 0x0B
#define FSP_BIT_NO_ROTATION BIT(3)
#define FSP_REG_PAGE_CTRL 0x0F
/* Finger-sensing Pad control registers */
#define FSP_REG_SYSCTL1 0x10
#define FSP_BIT_EN_REG_CLK BIT(5)
#define FSP_REG_TMOD_STATUS 0x20
#define FSP_REG_OPC_QDOWN 0x31
#define FSP_BIT_EN_OPC_TAG BIT(7)
#define FSP_REG_OPTZ_XLO 0x34
#define FSP_REG_OPTZ_XHI 0x35
#define FSP_REG_OPTZ_YLO 0x36
#define FSP_REG_OPTZ_YHI 0x37
#define FSP_REG_SYSCTL5 0x40
#define FSP_BIT_90_DEGREE BIT(0)
#define FSP_BIT_EN_MSID6 BIT(1)
#define FSP_BIT_EN_MSID7 BIT(2)
#define FSP_BIT_EN_MSID8 BIT(3)
#define FSP_BIT_EN_AUTO_MSID8 BIT(5)
#define FSP_BIT_EN_PKT_G0 BIT(6)
#define FSP_REG_ONPAD_CTL 0x43
#define FSP_BIT_ONPAD_ENABLE BIT(0)
#define FSP_BIT_ONPAD_FBBB BIT(1)
#define FSP_BIT_FIX_VSCR BIT(3)
#define FSP_BIT_FIX_HSCR BIT(5)
#define FSP_BIT_DRAG_LOCK BIT(6)
#define FSP_REG_SWC1 (0x90)
#define FSP_BIT_SWC1_EN_ABS_1F BIT(0)
#define FSP_BIT_SWC1_EN_GID BIT(1)
#define FSP_BIT_SWC1_EN_ABS_2F BIT(2)
#define FSP_BIT_SWC1_EN_FUP_OUT BIT(3)
#define FSP_BIT_SWC1_EN_ABS_CON BIT(4)
#define FSP_BIT_SWC1_GST_GRP0 BIT(5)
#define FSP_BIT_SWC1_GST_GRP1 BIT(6)
#define FSP_BIT_SWC1_BX_COMPAT BIT(7)
#define FSP_PAGE_0B (0x0b)
#define FSP_PAGE_82 (0x82)
#define FSP_PAGE_DEFAULT FSP_PAGE_82
#define FSP_REG_SN0 (0x40)
#define FSP_REG_SN1 (0x41)
#define FSP_REG_SN2 (0x42)
/* Finger-sensing Pad packet formating related definitions */
/* absolute packet type */
#define FSP_PKT_TYPE_NORMAL (0x00)
#define FSP_PKT_TYPE_ABS (0x01)
#define FSP_PKT_TYPE_NOTIFY (0x02)
#define FSP_PKT_TYPE_NORMAL_OPC (0x03)
#define FSP_PKT_TYPE_SHIFT (6)
/* bit definitions for the first byte of report packet */
#define FSP_PB0_LBTN BIT(0)
#define FSP_PB0_RBTN BIT(1)
#define FSP_PB0_MBTN BIT(2)
#define FSP_PB0_MFMC_FGR2 FSP_PB0_MBTN
#define FSP_PB0_MUST_SET BIT(3)
#define FSP_PB0_PHY_BTN BIT(4)
#define FSP_PB0_MFMC BIT(5)
/* hardware revisions */
#define FSP_VER_STL3888_A4 (0xC1)
#define FSP_VER_STL3888_B0 (0xD0)
#define FSP_VER_STL3888_B1 (0xD1)
#define FSP_VER_STL3888_B2 (0xD2)
#define FSP_VER_STL3888_C0 (0xE0)
#define FSP_VER_STL3888_C1 (0xE1)
#define FSP_VER_STL3888_D0 (0xE2)
#define FSP_VER_STL3888_D1 (0xE3)
#define FSP_VER_STL3888_E0 (0xE4)
#ifdef __KERNEL__
struct fsp_data {
unsigned char ver; /* hardware version */
unsigned char rev; /* hardware revison */
unsigned int buttons; /* Number of buttons */
unsigned int flags;
#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */
bool vscroll; /* Vertical scroll zone enabled */
bool hscroll; /* Horizontal scroll zone enabled */
unsigned char last_reg; /* Last register we requested read from */
unsigned char last_val;
unsigned int last_mt_fgr; /* Last seen finger(multitouch) */
};
extern int fsp_detect(struct psmouse *psmouse, bool set_properties);
extern int fsp_init(struct psmouse *psmouse);
#endif /* __KERNEL__ */
#endif /* !__SENTELIC_H */
+340
View File
@@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Serial mouse driver for Linux
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#define DRIVER_DESC "Serial mouse driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
"Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
"Logitech MZ++ Mouse"};
struct sermouse {
struct input_dev *dev;
signed char buf[8];
unsigned char count;
unsigned char type;
unsigned long last;
char phys[32];
};
/*
* sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and
* applies some prediction to the data, resulting in 96 updates per
* second, which is as good as a PS/2 or USB mouse.
*/
static void sermouse_process_msc(struct sermouse *sermouse, signed char data)
{
struct input_dev *dev = sermouse->dev;
signed char *buf = sermouse->buf;
switch (sermouse->count) {
case 0:
if ((data & 0xf8) != 0x80)
return;
input_report_key(dev, BTN_LEFT, !(data & 4));
input_report_key(dev, BTN_RIGHT, !(data & 1));
input_report_key(dev, BTN_MIDDLE, !(data & 2));
break;
case 1:
case 3:
input_report_rel(dev, REL_X, data / 2);
input_report_rel(dev, REL_Y, -buf[1]);
buf[0] = data - data / 2;
break;
case 2:
case 4:
input_report_rel(dev, REL_X, buf[0]);
input_report_rel(dev, REL_Y, buf[1] - data);
buf[1] = data / 2;
break;
}
input_sync(dev);
if (++sermouse->count == 5)
sermouse->count = 0;
}
/*
* sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and
* generates events. With prediction it gets 80 updates/sec, assuming
* standard 3-byte packets and 1200 bps.
*/
static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
{
struct input_dev *dev = sermouse->dev;
signed char *buf = sermouse->buf;
if (data & 0x40)
sermouse->count = 0;
else if (sermouse->count == 0)
return;
switch (sermouse->count) {
case 0:
buf[1] = data;
input_report_key(dev, BTN_LEFT, (data >> 5) & 1);
input_report_key(dev, BTN_RIGHT, (data >> 4) & 1);
break;
case 1:
buf[2] = data;
data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f));
input_report_rel(dev, REL_X, data / 2);
input_report_rel(dev, REL_Y, buf[4]);
buf[3] = data - data / 2;
break;
case 2:
/* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */
if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1]))
input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key));
buf[0] = buf[1];
data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f));
input_report_rel(dev, REL_X, buf[3]);
input_report_rel(dev, REL_Y, data - buf[4]);
buf[4] = data / 2;
break;
case 3:
switch (sermouse->type) {
case SERIO_MS:
sermouse->type = SERIO_MP;
fallthrough;
case SERIO_MP:
if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */
input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1);
input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
break;
case SERIO_MZP:
case SERIO_MZPP:
input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
fallthrough;
case SERIO_MZ:
input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
input_report_rel(dev, REL_WHEEL, (data & 8) - (data & 7));
break;
}
break;
case 4:
case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */
buf[1] = (data >> 2) & 0x0f;
break;
case 5:
case 7: /* Ignore anything besides MZ++ */
if (sermouse->type != SERIO_MZPP)
break;
switch (buf[1]) {
case 1: /* Extra mouse info */
input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (data >> 5) & 1);
input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8));
break;
default: /* We don't decode anything else yet. */
printk(KERN_WARNING
"sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]);
break;
}
break;
}
input_sync(dev);
sermouse->count++;
}
/*
* sermouse_interrupt() handles incoming characters, either gathering them into
* packets or passing them to the command routine as command output.
*/
static irqreturn_t sermouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct sermouse *sermouse = serio_get_drvdata(serio);
if (time_after(jiffies, sermouse->last + HZ/10))
sermouse->count = 0;
sermouse->last = jiffies;
if (sermouse->type > SERIO_SUN)
sermouse_process_ms(sermouse, data);
else
sermouse_process_msc(sermouse, data);
return IRQ_HANDLED;
}
/*
* sermouse_disconnect() cleans up after we don't want talk
* to the mouse anymore.
*/
static void sermouse_disconnect(struct serio *serio)
{
struct sermouse *sermouse = serio_get_drvdata(serio);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_unregister_device(sermouse->dev);
kfree(sermouse);
}
/*
* sermouse_connect() is a callback form the serio module when
* an unhandled serio port is found.
*/
static int sermouse_connect(struct serio *serio, struct serio_driver *drv)
{
struct sermouse *sermouse;
struct input_dev *input_dev;
unsigned char c = serio->id.extra;
int err = -ENOMEM;
sermouse = kzalloc_obj(*sermouse);
input_dev = input_allocate_device();
if (!sermouse || !input_dev)
goto fail1;
sermouse->dev = input_dev;
snprintf(sermouse->phys, sizeof(sermouse->phys), "%s/input0", serio->phys);
sermouse->type = serio->id.proto;
input_dev->name = sermouse_protocols[sermouse->type];
input_dev->phys = sermouse->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = sermouse->type;
input_dev->id.product = c;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
if (c & 0x01) set_bit(BTN_MIDDLE, input_dev->keybit);
if (c & 0x02) set_bit(BTN_SIDE, input_dev->keybit);
if (c & 0x04) set_bit(BTN_EXTRA, input_dev->keybit);
if (c & 0x10) set_bit(REL_WHEEL, input_dev->relbit);
if (c & 0x20) set_bit(REL_HWHEEL, input_dev->relbit);
serio_set_drvdata(serio, sermouse);
err = serio_open(serio, drv);
if (err)
goto fail2;
err = input_register_device(sermouse->dev);
if (err)
goto fail3;
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(sermouse);
return err;
}
static struct serio_device_id sermouse_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_MSC,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_SUN,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_MS,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_MP,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_MZ,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_MZP,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_MZPP,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, sermouse_serio_ids);
static struct serio_driver sermouse_drv = {
.driver = {
.name = "sermouse",
},
.description = DRIVER_DESC,
.id_table = sermouse_serio_ids,
.interrupt = sermouse_interrupt,
.connect = sermouse_connect,
.disconnect = sermouse_disconnect,
};
module_serio_driver(sermouse_drv);
File diff suppressed because it is too large Load Diff
+215
View File
@@ -0,0 +1,215 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Synaptics TouchPad PS/2 mouse driver
*/
#ifndef _SYNAPTICS_H
#define _SYNAPTICS_H
/* synaptics queries */
#define SYN_QUE_IDENTIFY 0x00
#define SYN_QUE_MODES 0x01
#define SYN_QUE_CAPABILITIES 0x02
#define SYN_QUE_MODEL 0x03
#define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06
#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
#define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09
#define SYN_QUE_FIRMWARE_ID 0x0a
#define SYN_QUE_EXT_CAPAB_0C 0x0c
#define SYN_QUE_EXT_MAX_COORDS 0x0d
#define SYN_QUE_EXT_MIN_COORDS 0x0f
#define SYN_QUE_MEXT_CAPAB_10 0x10
/* synaptics modes */
#define SYN_BIT_ABSOLUTE_MODE BIT(7)
#define SYN_BIT_HIGH_RATE BIT(6)
#define SYN_BIT_SLEEP_MODE BIT(3)
#define SYN_BIT_DISABLE_GESTURE BIT(2)
#define SYN_BIT_FOUR_BYTE_CLIENT BIT(1)
#define SYN_BIT_W_MODE BIT(0)
/* synaptics model ID bits */
#define SYN_MODEL_ROT180(m) ((m) & BIT(23))
#define SYN_MODEL_PORTRAIT(m) ((m) & BIT(22))
#define SYN_MODEL_SENSOR(m) (((m) & GENMASK(21, 16)) >> 16)
#define SYN_MODEL_HARDWARE(m) (((m) & GENMASK(15, 9)) >> 9)
#define SYN_MODEL_NEWABS(m) ((m) & BIT(7))
#define SYN_MODEL_PEN(m) ((m) & BIT(6))
#define SYN_MODEL_SIMPLIC(m) ((m) & BIT(5))
#define SYN_MODEL_GEOMETRY(m) ((m) & GENMASK(3, 0))
/* synaptics capability bits */
#define SYN_CAP_EXTENDED(c) ((c) & BIT(23))
#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & BIT(18))
#define SYN_CAP_PASS_THROUGH(c) ((c) & BIT(7))
#define SYN_CAP_SLEEP(c) ((c) & BIT(4))
#define SYN_CAP_FOUR_BUTTON(c) ((c) & BIT(3))
#define SYN_CAP_MULTIFINGER(c) ((c) & BIT(1))
#define SYN_CAP_PALMDETECT(c) ((c) & BIT(0))
#define SYN_CAP_SUBMODEL_ID(c) (((c) & GENMASK(15, 8)) >> 8)
#define SYN_EXT_CAP_REQUESTS(c) (((c) & GENMASK(22, 20)) >> 20)
#define SYN_CAP_MB_MASK GENMASK(15, 12)
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & SYN_CAP_MB_MASK) >> 12)
#define SYN_CAP_PRODUCT_ID(ec) (((ec) & GENMASK(23, 16)) >> 16)
#define SYN_MEXT_CAP_BIT(m) ((m) & BIT(1))
/*
* The following describes response for the 0x0c query.
*
* byte mask name meaning
* ---- ---- ------- ------------
* 1 0x01 adjustable threshold capacitive button sensitivity
* can be adjusted
* 1 0x02 report max query 0x0d gives max coord reported
* 1 0x04 clearpad sensor is ClearPad product
* 1 0x08 advanced gesture not particularly meaningful
* 1 0x10 clickpad bit 0 1-button ClickPad
* 1 0x60 multifinger mode identifies firmware finger counting
* (not reporting!) algorithm.
* Not particularly meaningful
* 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered
* 2 0x01 clickpad bit 1 2-button ClickPad
* 2 0x02 deluxe LED controls touchpad support LED commands
* ala multimedia control bar
* 2 0x04 reduced filtering firmware does less filtering on
* position data, driver should watch
* for noise.
* 2 0x08 image sensor image sensor tracks 5 fingers, but only
* reports 2.
* 2 0x01 uniform clickpad whole clickpad moves instead of being
* hinged at the top.
* 2 0x20 report min query 0x0f gives min coord reported
*/
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & BIT(20)) /* 1-button ClickPad */
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & BIT(8)) /* 2-button ClickPad */
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & BIT(17))
#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & BIT(13))
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & BIT(19))
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & BIT(10))
#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & BIT(11))
#define SYN_CAP_INTERTOUCH(ex0c) ((ex0c) & BIT(14))
/*
* The following descibes response for the 0x10 query.
*
* byte mask name meaning
* ---- ---- ------- ------------
* 1 0x01 ext buttons are stick buttons exported in the extended
* capability are actually meant to be used
* by the tracktick (pass-through).
* 1 0x02 SecurePad the touchpad is a SecurePad, so it
* contains a built-in fingerprint reader.
* 1 0xe0 more ext count how many more extented queries are
* available after this one.
* 2 0xff SecurePad width the width of the SecurePad fingerprint
* reader.
* 3 0xff SecurePad height the height of the SecurePad fingerprint
* reader.
*/
#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & BIT(16))
#define SYN_CAP_SECUREPAD(ex10) ((ex10) & BIT(17))
#define SYN_EXT_BUTTON_STICK_L(eb) (((eb) & BIT(0)) >> 0)
#define SYN_EXT_BUTTON_STICK_M(eb) (((eb) & BIT(1)) >> 1)
#define SYN_EXT_BUTTON_STICK_R(eb) (((eb) & BIT(2)) >> 2)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & BIT(7))
#define SYN_MODE_RATE(m) ((m) & BIT(6))
#define SYN_MODE_BAUD_SLEEP(m) ((m) & BIT(3))
#define SYN_MODE_DISABLE_GESTURE(m) ((m) & BIT(2))
#define SYN_MODE_PACKSIZE(m) ((m) & BIT(1))
#define SYN_MODE_WMODE(m) ((m) & BIT(0))
/* synaptics identify query bits */
#define SYN_ID_MODEL(i) (((i) & GENMASK(7, 4)) >> 4)
#define SYN_ID_MAJOR(i) (((i) & GENMASK(3, 0)) >> 0)
#define SYN_ID_MINOR(i) (((i) & GENMASK(23, 16)) >> 16)
#define SYN_ID_FULL(i) ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i))
#define SYN_ID_IS_SYNAPTICS(i) (((i) & GENMASK(15, 8)) == 0x004700U)
#define SYN_ID_DISGEST_SUPPORTED(i) (SYN_ID_MAJOR(i) >= 4)
/* synaptics special commands */
#define SYN_PS_SET_MODE2 0x14
#define SYN_PS_CLIENT_CMD 0x28
/* amount to fuzz position data when touchpad reports reduced filtering */
#define SYN_REDUCED_FILTER_FUZZ 8
/* synaptics packet types */
enum synaptics_pkt_type {
SYN_NEWABS,
SYN_NEWABS_STRICT,
SYN_NEWABS_RELAXED,
SYN_OLDABS,
};
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
struct synaptics_hw_state {
int x;
int y;
int z;
int w;
unsigned int left:1;
unsigned int right:1;
unsigned int middle:1;
unsigned int up:1;
unsigned int down:1;
u8 ext_buttons;
s8 scroll;
};
/* Data read from the touchpad */
struct synaptics_device_info {
u32 model_id; /* Model-ID */
u32 firmware_id; /* Firmware-ID */
u32 board_id; /* Board-ID */
u32 capabilities; /* Capabilities */
u32 ext_cap; /* Extended Capabilities */
u32 ext_cap_0c; /* Ext Caps from 0x0c query */
u32 ext_cap_10; /* Ext Caps from 0x10 query */
u32 identity; /* Identification */
u32 x_res, y_res; /* X/Y resolution in units/mm */
u32 x_max, y_max; /* Max coordinates (from FW) */
u32 x_min, y_min; /* Min coordinates (from FW) */
};
struct synaptics_data {
struct synaptics_device_info info;
enum synaptics_pkt_type pkt_type; /* packet type - old, new, etc */
u8 mode; /* current mode byte */
int scroll;
bool absolute_mode; /* run in Absolute mode */
bool disable_gesture; /* disable gestures */
struct serio *pt_port; /* Pass-through serio port */
bool pt_port_open;
/*
* Last received Advanced Gesture Mode (AGM) packet. An AGM packet
* contains position data for a second contact, at half resolution.
*/
struct synaptics_hw_state agm;
unsigned int agm_count; /* finger count reported by agm */
/* ForcePad handling */
unsigned long press_start;
bool press;
bool report_press;
bool is_forcepad;
};
void synaptics_module_init(void);
int synaptics_detect(struct psmouse *psmouse, bool set_properties);
int synaptics_init_absolute(struct psmouse *psmouse);
int synaptics_init_relative(struct psmouse *psmouse);
int synaptics_init_smbus(struct psmouse *psmouse);
int synaptics_init(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
#endif /* _SYNAPTICS_H */
@@ -0,0 +1,639 @@
/*
* Synaptics touchpad with I2C interface
*
* Copyright (C) 2009 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
* Igor Grinberg <grinberg@compulab.co.il>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/pm.h>
#define DRIVER_NAME "synaptics_i2c"
/* maximum product id is 15 characters */
#define PRODUCT_ID_LENGTH 15
#define REGISTER_LENGTH 8
/*
* after soft reset, we should wait for 1 ms
* before the device becomes operational
*/
#define SOFT_RESET_DELAY_US 3000
/* and after hard reset, we should wait for max 500ms */
#define HARD_RESET_DELAY_MS 500
/* Registers by SMBus address */
#define PAGE_SEL_REG 0xff
#define DEVICE_STATUS_REG 0x09
/* Registers by RMI address */
#define DEV_CONTROL_REG 0x0000
#define INTERRUPT_EN_REG 0x0001
#define ERR_STAT_REG 0x0002
#define INT_REQ_STAT_REG 0x0003
#define DEV_COMMAND_REG 0x0004
#define RMI_PROT_VER_REG 0x0200
#define MANUFACT_ID_REG 0x0201
#define PHYS_INT_VER_REG 0x0202
#define PROD_PROPERTY_REG 0x0203
#define INFO_QUERY_REG0 0x0204
#define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1)
#define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2)
#define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3)
#define PRODUCT_ID_REG0 0x0210
#define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1)
#define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2)
#define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3)
#define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4)
#define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5)
#define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6)
#define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7)
#define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8)
#define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9)
#define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10)
#define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11)
#define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12)
#define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13)
#define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14)
#define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15)
#define DATA_REG0 0x0400
#define ABS_PRESSURE_REG 0x0401
#define ABS_MSB_X_REG 0x0402
#define ABS_LSB_X_REG (ABS_MSB_X_REG + 1)
#define ABS_MSB_Y_REG 0x0404
#define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1)
#define REL_X_REG 0x0406
#define REL_Y_REG 0x0407
#define DEV_QUERY_REG0 0x1000
#define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1)
#define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2)
#define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3)
#define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4)
#define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5)
#define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6)
#define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7)
#define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8)
#define GENERAL_2D_CONTROL_REG 0x1041
#define SENSOR_SENSITIVITY_REG 0x1044
#define SENS_MAX_POS_MSB_REG 0x1046
#define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1)
/* Register bits */
/* Device Control Register Bits */
#define REPORT_RATE_1ST_BIT 6
/* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */
#define F10_ABS_INT_ENA 0
#define F10_REL_INT_ENA 1
#define F20_INT_ENA 2
/* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */
#define F10_ABS_INT_REQ 0
#define F10_REL_INT_REQ 1
#define F20_INT_REQ 2
/* Device Status Register Bits (DEVICE_STATUS_REG) */
#define STAT_CONFIGURED 6
#define STAT_ERROR 7
/* Device Command Register Bits (DEV_COMMAND_REG) */
#define RESET_COMMAND 0x01
#define REZERO_COMMAND 0x02
/* Data Register 0 Bits (DATA_REG0) */
#define GESTURE 3
/* Device Query Registers Bits */
/* DEV_QUERY_REG3 */
#define HAS_PALM_DETECT 1
#define HAS_MULTI_FING 2
#define HAS_SCROLLER 4
#define HAS_2D_SCROLL 5
/* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */
#define NO_DECELERATION 1
#define REDUCE_REPORTING 3
#define NO_FILTER 5
/* Function Masks */
/* Device Control Register Masks (DEV_CONTROL_REG) */
#define REPORT_RATE_MSK 0xc0
#define SLEEP_MODE_MSK 0x07
/* Device Sleep Modes */
#define FULL_AWAKE 0x0
#define NORMAL_OP 0x1
#define LOW_PWR_OP 0x2
#define VERY_LOW_PWR_OP 0x3
#define SENS_SLEEP 0x4
#define SLEEP_MOD 0x5
#define DEEP_SLEEP 0x6
#define HIBERNATE 0x7
/* Interrupt Register Mask */
/* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */
#define INT_ENA_REQ_MSK 0x07
#define INT_ENA_ABS_MSK 0x01
#define INT_ENA_REL_MSK 0x02
#define INT_ENA_F20_MSK 0x04
/* Device Status Register Masks (DEVICE_STATUS_REG) */
#define CONFIGURED_MSK 0x40
#define ERROR_MSK 0x80
/* Data Register 0 Masks */
#define FINGER_WIDTH_MSK 0xf0
#define GESTURE_MSK 0x08
#define SENSOR_STATUS_MSK 0x07
/*
* MSB Position Register Masks
* ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG |
* DEV_QUERY_REG3 | DEV_QUERY_REG5
*/
#define MSB_POSITION_MSK 0x1f
/* Device Query Registers Masks */
/* DEV_QUERY_REG2 */
#define NUM_EXTRA_POS_MSK 0x07
/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */
#define THREAD_IRQ_SLEEP_SECS 2
#define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC)
/*
* When in Polling mode and no data received for NO_DATA_THRES msecs
* reduce the polling rate to NO_DATA_SLEEP_MSECS
*/
#define NO_DATA_THRES (MSEC_PER_SEC)
#define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4)
/* Control touchpad's No Deceleration option */
static bool no_decel = true;
module_param(no_decel, bool, 0644);
MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)");
/* Control touchpad's Reduced Reporting option */
static bool reduce_report;
module_param(reduce_report, bool, 0644);
MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)");
/* Control touchpad's No Filter option */
static bool no_filter;
module_param(no_filter, bool, 0644);
MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)");
/*
* touchpad Attention line is Active Low and Open Drain,
* therefore should be connected to pulled up line
* and the irq configuration should be set to Falling Edge Trigger
*/
/* Control IRQ / Polling option */
static bool polling_req;
module_param(polling_req, bool, 0444);
MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)");
/* Control Polling Rate */
static int scan_rate = 80;
module_param(scan_rate, int, 0644);
MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80");
/* The main device structure */
struct synaptics_i2c {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
int no_data_count;
int no_decel_param;
int reduce_report_param;
int no_filter_param;
int scan_rate_param;
int scan_ms;
};
static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate)
{
touch->scan_ms = MSEC_PER_SEC / scan_rate;
touch->scan_rate_param = scan_rate;
}
/*
* Driver's initial design makes no race condition possible on i2c bus,
* so there is no need in any locking.
* Keep it in mind, while playing with the code.
*/
static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg)
{
int error;
error = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (error)
return error;
return i2c_smbus_read_byte_data(client, reg & 0xff);
}
static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val)
{
int error;
error = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (error)
return error;
error = i2c_smbus_write_byte_data(client, reg & 0xff, val);
if (error)
return error;
return error;
}
static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg)
{
int error;
error = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (error)
return error;
return i2c_smbus_read_word_data(client, reg & 0xff);
}
static int synaptics_i2c_config(struct i2c_client *client)
{
int control;
int error;
u8 int_en;
/* set Report Rate to Device Highest (>=80) and Sleep to normal */
error = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1);
if (error)
return error;
/* set Interrupt Disable to Func20 / Enable to Func10) */
int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK;
error = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en);
if (error)
return error;
control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG);
/* No Deceleration */
control |= no_decel ? 1 << NO_DECELERATION : 0;
/* Reduced Reporting */
control |= reduce_report ? 1 << REDUCE_REPORTING : 0;
/* No Filter */
control |= no_filter ? 1 << NO_FILTER : 0;
error = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control);
if (error)
return error;
return 0;
}
static int synaptics_i2c_reset_config(struct i2c_client *client)
{
int error;
/* Reset the Touchpad */
error = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND);
if (error) {
dev_err(&client->dev, "Unable to reset device\n");
return error;
}
usleep_range(SOFT_RESET_DELAY_US, SOFT_RESET_DELAY_US + 100);
error = synaptics_i2c_config(client);
if (error) {
dev_err(&client->dev, "Unable to config device\n");
return error;
}
return 0;
}
static int synaptics_i2c_check_error(struct i2c_client *client)
{
int status;
int error;
status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) &
(CONFIGURED_MSK | ERROR_MSK);
if (status != CONFIGURED_MSK) {
error = synaptics_i2c_reset_config(client);
if (error)
return error;
}
return 0;
}
static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
{
struct input_dev *input = touch->input;
int xy_delta, gesture;
s32 data;
s8 x_delta, y_delta;
/* Deal with spontaneous resets and errors */
if (synaptics_i2c_check_error(touch->client))
return false;
/* Get Gesture Bit */
data = synaptics_i2c_reg_get(touch->client, DATA_REG0);
gesture = (data >> GESTURE) & 0x1;
/*
* Get Relative axes. we have to get them in one shot,
* so we get 2 bytes starting from REL_X_REG.
*/
xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff;
/* Separate X from Y */
x_delta = xy_delta & 0xff;
y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff;
/* Report the button event */
input_report_key(input, BTN_LEFT, gesture);
/* Report the deltas */
input_report_rel(input, REL_X, x_delta);
input_report_rel(input, REL_Y, -y_delta);
input_sync(input);
return xy_delta || gesture;
}
static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
{
struct synaptics_i2c *touch = dev_id;
mod_delayed_work(system_dfl_wq, &touch->dwork, 0);
return IRQ_HANDLED;
}
static void synaptics_i2c_check_params(struct synaptics_i2c *touch)
{
bool reset = false;
if (scan_rate != touch->scan_rate_param)
set_scan_rate(touch, scan_rate);
if (no_decel != touch->no_decel_param) {
touch->no_decel_param = no_decel;
reset = true;
}
if (no_filter != touch->no_filter_param) {
touch->no_filter_param = no_filter;
reset = true;
}
if (reduce_report != touch->reduce_report_param) {
touch->reduce_report_param = reduce_report;
reset = true;
}
if (reset)
synaptics_i2c_reset_config(touch->client);
}
/* Control the Device polling rate / Work Handler sleep time */
static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
bool have_data)
{
unsigned long delay, nodata_count_thres;
if (polling_req) {
delay = touch->scan_ms;
if (have_data) {
touch->no_data_count = 0;
} else {
nodata_count_thres = NO_DATA_THRES / touch->scan_ms;
if (touch->no_data_count < nodata_count_thres)
touch->no_data_count++;
else
delay = NO_DATA_SLEEP_MSECS;
}
return msecs_to_jiffies(delay);
}
delay = msecs_to_jiffies(THREAD_IRQ_SLEEP_MSECS);
return round_jiffies_relative(delay);
}
/* Work Handler */
static void synaptics_i2c_work_handler(struct work_struct *work)
{
bool have_data;
struct synaptics_i2c *touch =
container_of(work, struct synaptics_i2c, dwork.work);
unsigned long delay;
synaptics_i2c_check_params(touch);
have_data = synaptics_i2c_get_input(touch);
delay = synaptics_i2c_adjust_delay(touch, have_data);
/*
* While interrupt driven, there is no real need to poll the device.
* But touchpads are very sensitive, so there could be errors
* related to physical environment and the attention line isn't
* necessarily asserted. In such case we can lose the touchpad.
* We poll the device once in THREAD_IRQ_SLEEP_SECS and
* if error is detected, we try to reset and reconfigure the touchpad.
*/
mod_delayed_work(system_dfl_wq, &touch->dwork, delay);
}
static int synaptics_i2c_open(struct input_dev *input)
{
struct synaptics_i2c *touch = input_get_drvdata(input);
int error;
error = synaptics_i2c_reset_config(touch->client);
if (error)
return error;
if (polling_req)
mod_delayed_work(system_dfl_wq, &touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
static void synaptics_i2c_close(struct input_dev *input)
{
struct synaptics_i2c *touch = input_get_drvdata(input);
if (!polling_req)
synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0);
cancel_delayed_work_sync(&touch->dwork);
/* Save some power */
synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP);
}
static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch)
{
struct input_dev *input = touch->input;
input->name = touch->client->name;
input->phys = touch->client->adapter->name;
input->id.bustype = BUS_I2C;
input->id.version = synaptics_i2c_word_get(touch->client,
INFO_QUERY_REG0);
input->open = synaptics_i2c_open;
input->close = synaptics_i2c_close;
input_set_drvdata(input, touch);
/* Register the device as mouse */
input_set_capability(input, EV_REL, REL_X);
input_set_capability(input, EV_REL, REL_Y);
/* Register device's buttons and keys */
input_set_capability(input, EV_KEY, BTN_LEFT);
}
static int synaptics_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct synaptics_i2c *touch;
int error;
touch = devm_kzalloc(dev, sizeof(*touch), GFP_KERNEL);
if (!touch)
return -ENOMEM;
touch->client = client;
touch->no_decel_param = no_decel;
touch->scan_rate_param = scan_rate;
set_scan_rate(touch, scan_rate);
INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
error = synaptics_i2c_reset_config(client);
if (error)
return error;
if (client->irq <= 0)
polling_req = true;
touch->input = devm_input_allocate_device(dev);
if (!touch->input)
return -ENOMEM;
synaptics_i2c_set_input_params(touch);
if (!polling_req) {
dev_dbg(dev, "Requesting IRQ: %d\n", client->irq);
error = devm_request_irq(dev, client->irq, synaptics_i2c_irq,
IRQ_TYPE_EDGE_FALLING,
DRIVER_NAME, touch);
if (error) {
dev_warn(dev, "IRQ request failed: %d, falling back to polling\n",
error);
polling_req = true;
synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, 0);
}
}
if (polling_req)
dev_dbg(dev, "Using polling at rate: %d times/sec\n", scan_rate);
/* Register the device in input subsystem */
error = input_register_device(touch->input);
if (error) {
dev_err(dev, "Input device register failed: %d\n", error);
return error;
}
i2c_set_clientdata(client, touch);
return 0;
}
static int synaptics_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct synaptics_i2c *touch = i2c_get_clientdata(client);
cancel_delayed_work_sync(&touch->dwork);
/* Save some power */
synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP);
return 0;
}
static int synaptics_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct synaptics_i2c *touch = i2c_get_clientdata(client);
struct input_dev *input = touch->input;
int error;
error = synaptics_i2c_reset_config(client);
if (error)
return error;
guard(mutex)(&input->mutex);
if (input_device_enabled(input))
mod_delayed_work(system_dfl_wq, &touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend,
synaptics_i2c_resume);
static const struct i2c_device_id synaptics_i2c_id_table[] = {
{ .name = "synaptics_i2c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
#ifdef CONFIG_OF
static const struct of_device_id synaptics_i2c_of_match[] = {
{ .compatible = "synaptics,synaptics_i2c", },
{ },
};
MODULE_DEVICE_TABLE(of, synaptics_i2c_of_match);
#endif
static struct i2c_driver synaptics_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(synaptics_i2c_of_match),
.pm = pm_sleep_ptr(&synaptics_i2c_pm),
},
.probe = synaptics_i2c_probe,
.id_table = synaptics_i2c_id_table,
};
module_i2c_driver(synaptics_i2c_driver);
MODULE_DESCRIPTION("Synaptics I2C touchpad driver");
MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab");
MODULE_LICENSE("GPL");
@@ -0,0 +1,546 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* USB Synaptics device driver
*
* Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk)
* Copyright (c) 2003 Ron Lee (ron@debian.org)
* cPad driver for kernel 2.4
*
* Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de)
* Copyright (c) 2004 Ron Lee (ron@debian.org)
* rewritten for kernel 2.6
*
* cPad display character device part is not included. It can be found at
* http://jan-steinhoff.de/linux/synaptics-usb.html
*
* Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman
* drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik
* drivers/input/mouse/synaptics.c by Peter Osterlund
*
* Trademarks are the property of their respective owners.
*/
/*
* There are three different types of Synaptics USB devices: Touchpads,
* touchsticks (or trackpoints), and touchscreens. Touchpads are well supported
* by this driver, touchstick support has not been tested much yet, and
* touchscreens have not been tested at all.
*
* Up to three alternate settings are possible:
* setting 0: one int endpoint for relative movement (used by usbhid.ko)
* setting 1: one int endpoint for absolute finger position
* setting 2 (cPad only): one int endpoint for absolute finger position and
* two bulk endpoints for the display (in/out)
* This driver uses setting 1.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb/input.h>
#define USB_VENDOR_ID_SYNAPTICS 0x06cb
#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */
#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */
#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */
#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */
#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */
#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */
#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */
#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */
#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */
#define SYNUSB_TOUCHPAD (1 << 0)
#define SYNUSB_STICK (1 << 1)
#define SYNUSB_TOUCHSCREEN (1 << 2)
#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */
#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */
#define SYNUSB_IO_ALWAYS (1 << 5)
#define USB_DEVICE_SYNAPTICS(prod, kind) \
USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \
USB_DEVICE_ID_SYNAPTICS_##prod), \
.driver_info = (kind),
#define SYNUSB_RECV_SIZE 8
#define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
struct synusb {
struct usb_device *udev;
struct usb_interface *intf;
struct urb *urb;
unsigned char *data;
/* serialize access to open/suspend */
struct mutex pm_mutex;
bool is_open;
/* input device related data structures */
struct input_dev *input;
char name[128];
char phys[64];
/* characteristics of the device */
unsigned long flags;
};
static void synusb_report_buttons(struct synusb *synusb)
{
struct input_dev *input_dev = synusb->input;
input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04);
input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01);
input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02);
}
static void synusb_report_stick(struct synusb *synusb)
{
struct input_dev *input_dev = synusb->input;
int x, y;
unsigned int pressure;
pressure = synusb->data[6];
x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7;
y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7;
if (pressure > 0) {
input_report_rel(input_dev, REL_X, x);
input_report_rel(input_dev, REL_Y, -y);
}
input_report_abs(input_dev, ABS_PRESSURE, pressure);
synusb_report_buttons(synusb);
input_sync(input_dev);
}
static void synusb_report_touchpad(struct synusb *synusb)
{
struct input_dev *input_dev = synusb->input;
unsigned int num_fingers, tool_width;
unsigned int x, y;
unsigned int pressure, w;
pressure = synusb->data[6];
x = be16_to_cpup((__be16 *)&synusb->data[2]);
y = be16_to_cpup((__be16 *)&synusb->data[4]);
w = synusb->data[0] & 0x0f;
if (pressure > 0) {
num_fingers = 1;
tool_width = 5;
switch (w) {
case 0 ... 1:
num_fingers = 2 + w;
break;
case 2: /* pen, pretend its a finger */
break;
case 4 ... 15:
tool_width = w;
break;
}
} else {
num_fingers = 0;
tool_width = 0;
}
/*
* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
* absolute -> relative conversion
*/
if (pressure > 30)
input_report_key(input_dev, BTN_TOUCH, 1);
if (pressure < 25)
input_report_key(input_dev, BTN_TOUCH, 0);
if (num_fingers > 0) {
input_report_abs(input_dev, ABS_X, x);
input_report_abs(input_dev, ABS_Y,
YMAX_NOMINAL + YMIN_NOMINAL - y);
}
input_report_abs(input_dev, ABS_PRESSURE, pressure);
input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width);
input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1);
input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
synusb_report_buttons(synusb);
if (synusb->flags & SYNUSB_AUXDISPLAY)
input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08);
input_sync(input_dev);
}
static void synusb_irq(struct urb *urb)
{
struct synusb *synusb = urb->context;
int error;
/* Check our status in case we need to bail out early. */
switch (urb->status) {
case 0:
usb_mark_last_busy(synusb->udev);
break;
/* Device went away so don't keep trying to read from it. */
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto resubmit;
break;
}
if (synusb->flags & SYNUSB_STICK)
synusb_report_stick(synusb);
else
synusb_report_touchpad(synusb);
resubmit:
error = usb_submit_urb(urb, GFP_ATOMIC);
if (error && error != -EPERM)
dev_err(&synusb->intf->dev,
"%s - usb_submit_urb failed with result: %d",
__func__, error);
}
static int synusb_open(struct input_dev *dev)
{
struct synusb *synusb = input_get_drvdata(dev);
int retval;
retval = usb_autopm_get_interface(synusb->intf);
if (retval) {
dev_err(&synusb->intf->dev,
"%s - usb_autopm_get_interface failed, error: %d\n",
__func__, retval);
return retval;
}
mutex_lock(&synusb->pm_mutex);
retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
if (retval) {
dev_err(&synusb->intf->dev,
"%s - usb_submit_urb failed, error: %d\n",
__func__, retval);
retval = -EIO;
goto out;
}
synusb->intf->needs_remote_wakeup = 1;
synusb->is_open = true;
out:
mutex_unlock(&synusb->pm_mutex);
usb_autopm_put_interface(synusb->intf);
return retval;
}
static void synusb_close(struct input_dev *dev)
{
struct synusb *synusb = input_get_drvdata(dev);
int autopm_error;
autopm_error = usb_autopm_get_interface(synusb->intf);
mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb);
synusb->intf->needs_remote_wakeup = 0;
synusb->is_open = false;
mutex_unlock(&synusb->pm_mutex);
if (!autopm_error)
usb_autopm_put_interface(synusb->intf);
}
static int synusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_endpoint_descriptor *ep;
struct synusb *synusb;
struct input_dev *input_dev;
unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber;
unsigned int altsetting = min(intf->num_altsetting, 1U);
int error;
error = usb_set_interface(udev, intf_num, altsetting);
if (error) {
dev_err(&udev->dev,
"Can not set alternate setting to %i, error: %i",
altsetting, error);
return error;
}
error = usb_find_int_in_endpoint(intf->cur_altsetting, &ep);
if (error)
return -ENODEV;
synusb = kzalloc_obj(*synusb);
input_dev = input_allocate_device();
if (!synusb || !input_dev) {
error = -ENOMEM;
goto err_free_mem;
}
synusb->udev = udev;
synusb->intf = intf;
synusb->input = input_dev;
mutex_init(&synusb->pm_mutex);
synusb->flags = id->driver_info;
if (synusb->flags & SYNUSB_COMBO) {
/*
* This is a combo device, we need to set proper
* capability, depending on the interface.
*/
synusb->flags |= intf_num == 1 ?
SYNUSB_STICK : SYNUSB_TOUCHPAD;
}
synusb->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!synusb->urb) {
error = -ENOMEM;
goto err_free_mem;
}
synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL,
&synusb->urb->transfer_dma);
if (!synusb->data) {
error = -ENOMEM;
goto err_free_urb;
}
usb_fill_int_urb(synusb->urb, udev,
usb_rcvintpipe(udev, ep->bEndpointAddress),
synusb->data, SYNUSB_RECV_SIZE,
synusb_irq, synusb,
ep->bInterval);
synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (udev->manufacturer)
strscpy(synusb->name, udev->manufacturer,
sizeof(synusb->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(synusb->name, " ", sizeof(synusb->name));
strlcat(synusb->name, udev->product, sizeof(synusb->name));
}
if (!strlen(synusb->name))
snprintf(synusb->name, sizeof(synusb->name),
"USB Synaptics Device %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
if (synusb->flags & SYNUSB_STICK)
strlcat(synusb->name, " (Stick)", sizeof(synusb->name));
usb_make_path(udev, synusb->phys, sizeof(synusb->phys));
strlcat(synusb->phys, "/input0", sizeof(synusb->phys));
input_dev->name = synusb->name;
input_dev->phys = synusb->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &synusb->intf->dev;
if (!(synusb->flags & SYNUSB_IO_ALWAYS)) {
input_dev->open = synusb_open;
input_dev->close = synusb_close;
}
input_set_drvdata(input_dev, synusb);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
if (synusb->flags & SYNUSB_STICK) {
__set_bit(EV_REL, input_dev->evbit);
__set_bit(REL_X, input_dev->relbit);
__set_bit(REL_Y, input_dev->relbit);
__set_bit(INPUT_PROP_POINTING_STICK, input_dev->propbit);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0);
} else {
input_set_abs_params(input_dev, ABS_X,
XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
input_set_abs_params(input_dev, ABS_Y,
YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
}
if (synusb->flags & SYNUSB_TOUCHSCREEN)
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
else
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit);
__set_bit(BTN_MIDDLE, input_dev->keybit);
usb_set_intfdata(intf, synusb);
if (synusb->flags & SYNUSB_IO_ALWAYS) {
error = synusb_open(input_dev);
if (error)
goto err_free_dma;
}
error = input_register_device(input_dev);
if (error) {
dev_err(&udev->dev,
"Failed to register input device, error %d\n",
error);
goto err_stop_io;
}
return 0;
err_stop_io:
if (synusb->flags & SYNUSB_IO_ALWAYS)
synusb_close(synusb->input);
err_free_dma:
usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
synusb->urb->transfer_dma);
err_free_urb:
usb_free_urb(synusb->urb);
err_free_mem:
input_free_device(input_dev);
kfree(synusb);
usb_set_intfdata(intf, NULL);
return error;
}
static void synusb_disconnect(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
struct usb_device *udev = interface_to_usbdev(intf);
if (synusb->flags & SYNUSB_IO_ALWAYS)
synusb_close(synusb->input);
input_unregister_device(synusb->input);
usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
synusb->urb->transfer_dma);
usb_free_urb(synusb->urb);
kfree(synusb);
usb_set_intfdata(intf, NULL);
}
static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct synusb *synusb = usb_get_intfdata(intf);
mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb);
mutex_unlock(&synusb->pm_mutex);
return 0;
}
static int synusb_resume(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
int retval = 0;
mutex_lock(&synusb->pm_mutex);
if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO;
}
mutex_unlock(&synusb->pm_mutex);
return retval;
}
static int synusb_pre_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb);
return 0;
}
static int synusb_post_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
int retval = 0;
if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO;
}
mutex_unlock(&synusb->pm_mutex);
return retval;
}
static int synusb_reset_resume(struct usb_interface *intf)
{
return synusb_resume(intf);
}
static const struct usb_device_id synusb_idtable[] = {
{ USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) },
{ USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) },
{ USB_DEVICE_SYNAPTICS(CPAD,
SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) },
{ USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) },
{ USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) },
{ USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) },
{ USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) },
{ USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) },
{ USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) },
{ }
};
MODULE_DEVICE_TABLE(usb, synusb_idtable);
static struct usb_driver synusb_driver = {
.name = "synaptics_usb",
.probe = synusb_probe,
.disconnect = synusb_disconnect,
.id_table = synusb_idtable,
.suspend = synusb_suspend,
.resume = synusb_resume,
.pre_reset = synusb_pre_reset,
.post_reset = synusb_post_reset,
.reset_resume = synusb_reset_resume,
.supports_autosuspend = 1,
};
module_usb_driver(synusb_driver);
MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, "
"Ron Lee <ron@debian.org>, "
"Jan Steinhoff <cpad@jan-steinhoff.de>");
MODULE_DESCRIPTION("Synaptics USB device driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* ----------------------------------------------------------------------------
* touchkit_ps2.c -- Driver for eGalax TouchKit PS/2 Touchscreens
*
* Copyright (C) 2005 by Stefan Lucke
* Copyright (C) 2004 by Daniel Ritz
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
*
* Based upon touchkitusb.c
*
* Vendor documentation is available at:
* http://home.eeti.com.tw/web20/drivers/Software%20Programming%20Guide_v2.0.pdf
*/
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "touchkit_ps2.h"
#define TOUCHKIT_MAX_XC 0x07ff
#define TOUCHKIT_MAX_YC 0x07ff
#define TOUCHKIT_CMD 0x0a
#define TOUCHKIT_CMD_LENGTH 1
#define TOUCHKIT_CMD_ACTIVE 'A'
#define TOUCHKIT_CMD_FIRMWARE_VERSION 'D'
#define TOUCHKIT_CMD_CONTROLLER_TYPE 'E'
#define TOUCHKIT_SEND_PARMS(s, r, c) ((s) << 12 | (r) << 8 | (c))
#define TOUCHKIT_GET_TOUCHED(packet) (((packet)[0]) & 0x01)
#define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2])
#define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4])
static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse)
{
unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev;
if (psmouse->pktcnt != 5)
return PSMOUSE_GOOD_DATA;
input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet));
input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet));
input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet));
input_sync(dev);
return PSMOUSE_FULL_PACKET;
}
int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties)
{
struct input_dev *dev = psmouse->dev;
unsigned char param[3];
int command;
param[0] = TOUCHKIT_CMD_LENGTH;
param[1] = TOUCHKIT_CMD_ACTIVE;
command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD);
if (ps2_command(&psmouse->ps2dev, param, command))
return -ENODEV;
if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 ||
param[2] != TOUCHKIT_CMD_ACTIVE)
return -ENODEV;
if (set_properties) {
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
dev->keybit[BIT_WORD(BTN_MOUSE)] = 0;
dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
psmouse->vendor = "eGalax";
psmouse->name = "Touchscreen";
psmouse->protocol_handler = touchkit_ps2_process_byte;
psmouse->pktsize = 5;
}
return 0;
}
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* ----------------------------------------------------------------------------
* touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens
*
* Copyright (C) 2005 by Stefan Lucke
* Copyright (c) 2005 Vojtech Pavlik
*/
#ifndef _TOUCHKIT_PS2_H
#define _TOUCHKIT_PS2_H
int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties);
#endif
@@ -0,0 +1,521 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Stephen Evanchik <evanchsa@gmail.com>
*
* Trademarks are the property of their respective owners.
*/
#include <linux/array_size.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/serio.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/libps2.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include "psmouse.h"
#include "trackpoint.h"
static const char * const trackpoint_variants[] = {
[TP_VARIANT_IBM] = "IBM",
[TP_VARIANT_ALPS] = "ALPS",
[TP_VARIANT_ELAN] = "Elan",
[TP_VARIANT_NXP] = "NXP",
[TP_VARIANT_JYT_SYNAPTICS] = "JYT_Synaptics",
[TP_VARIANT_SYNAPTICS] = "Synaptics",
};
/*
* Power-on Reset: Resets all trackpoint parameters, including RAM values,
* to defaults.
* Returns zero on success, non-zero on failure.
*/
static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
{
u8 param[2] = { TP_POR };
int err;
err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND));
if (err)
return err;
/* Check for success response -- 0xAA00 */
if (param[0] != 0xAA || param[1] != 0x00)
return -ENODEV;
return 0;
}
/*
* Device IO: read, write and toggle bit
*/
static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results)
{
results[0] = loc;
return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND));
}
static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val)
{
u8 param[3] = { TP_WRITE_MEM, loc, val };
return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
}
static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask)
{
u8 param[3] = { TP_TOGGLE, loc, mask };
/* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F)
return -EINVAL;
return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
}
static int trackpoint_update_bit(struct ps2dev *ps2dev,
u8 loc, u8 mask, u8 value)
{
int retval;
u8 data;
retval = trackpoint_read(ps2dev, loc, &data);
if (retval)
return retval;
if (((data & mask) == mask) != !!value)
retval = trackpoint_toggle_bit(ps2dev, loc, mask);
return retval;
}
/*
* Trackpoint-specific attributes
*/
struct trackpoint_attr_data {
size_t field_offset;
u8 command;
u8 mask;
bool inverted;
u8 power_on_default;
};
static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse,
void *data, char *buf)
{
struct trackpoint_data *tp = psmouse->private;
struct trackpoint_attr_data *attr = data;
u8 value = *(u8 *)((void *)tp + attr->field_offset);
if (attr->inverted)
value = !value;
return sprintf(buf, "%u\n", value);
}
static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
struct trackpoint_data *tp = psmouse->private;
struct trackpoint_attr_data *attr = data;
u8 *field = (void *)tp + attr->field_offset;
u8 value;
int err;
err = kstrtou8(buf, 10, &value);
if (err)
return err;
*field = value;
err = trackpoint_write(&psmouse->ps2dev, attr->command, value);
return err ?: count;
}
#define TRACKPOINT_INT_ATTR(_name, _command, _default) \
static struct trackpoint_attr_data trackpoint_attr_##_name = { \
.field_offset = offsetof(struct trackpoint_data, _name), \
.command = _command, \
.power_on_default = _default, \
}; \
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
&trackpoint_attr_##_name, \
trackpoint_show_int_attr, trackpoint_set_int_attr)
static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
struct trackpoint_data *tp = psmouse->private;
struct trackpoint_attr_data *attr = data;
bool *field = (void *)tp + attr->field_offset;
bool value;
int err;
err = kstrtobool(buf, &value);
if (err)
return err;
if (attr->inverted)
value = !value;
if (*field != value) {
*field = value;
err = trackpoint_toggle_bit(&psmouse->ps2dev,
attr->command, attr->mask);
}
return err ?: count;
}
#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \
static struct trackpoint_attr_data trackpoint_attr_##_name = { \
.field_offset = offsetof(struct trackpoint_data, \
_name), \
.command = _command, \
.mask = _mask, \
.inverted = _inv, \
.power_on_default = _default, \
}; \
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
&trackpoint_attr_##_name, \
trackpoint_show_int_attr, trackpoint_set_bit_attr)
TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS);
TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED);
TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA);
TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH);
TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS);
TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG);
TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH);
TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH);
TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME);
TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV);
TRACKPOINT_INT_ATTR(drift_time, TP_DRIFT_TIME, TP_DEF_DRIFT_TIME);
TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, false,
TP_DEF_PTSON);
TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, false,
TP_DEF_SKIPBACK);
TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, true,
TP_DEF_EXT_DEV);
static bool trackpoint_is_attr_available(struct psmouse *psmouse,
struct attribute *attr)
{
struct trackpoint_data *tp = psmouse->private;
return tp->variant_id == TP_VARIANT_IBM ||
attr == &psmouse_attr_sensitivity.dattr.attr ||
attr == &psmouse_attr_press_to_select.dattr.attr;
}
static umode_t trackpoint_is_attr_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct serio *serio = to_serio_port(dev);
struct psmouse *psmouse = psmouse_from_serio(serio);
return trackpoint_is_attr_available(psmouse, attr) ? attr->mode : 0;
}
static struct attribute *trackpoint_attrs[] = {
&psmouse_attr_sensitivity.dattr.attr,
&psmouse_attr_speed.dattr.attr,
&psmouse_attr_inertia.dattr.attr,
&psmouse_attr_reach.dattr.attr,
&psmouse_attr_draghys.dattr.attr,
&psmouse_attr_mindrag.dattr.attr,
&psmouse_attr_thresh.dattr.attr,
&psmouse_attr_upthresh.dattr.attr,
&psmouse_attr_ztime.dattr.attr,
&psmouse_attr_jenks.dattr.attr,
&psmouse_attr_drift_time.dattr.attr,
&psmouse_attr_press_to_select.dattr.attr,
&psmouse_attr_skipback.dattr.attr,
&psmouse_attr_ext_dev.dattr.attr,
NULL
};
static struct attribute_group trackpoint_attr_group = {
.is_visible = trackpoint_is_attr_visible,
.attrs = trackpoint_attrs,
};
#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \
do { \
struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \
\
if ((!_power_on || _tp->_name != _attr->power_on_default) && \
trackpoint_is_attr_available(_psmouse, \
&psmouse_attr_##_name.dattr.attr)) { \
if (!_attr->mask) \
trackpoint_write(&_psmouse->ps2dev, \
_attr->command, _tp->_name); \
else \
trackpoint_update_bit(&_psmouse->ps2dev, \
_attr->command, _attr->mask, \
_tp->_name); \
} \
} while (0)
#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \
do { \
_tp->_name = trackpoint_attr_##_name.power_on_default; \
} while (0)
static int trackpoint_start_protocol(struct psmouse *psmouse,
u8 *variant_id, u8 *firmware_id)
{
u8 param[2] = { 0 };
int error;
error = ps2_command(&psmouse->ps2dev,
param, MAKE_PS2_CMD(0, 2, TP_READ_ID));
if (error)
return error;
switch (param[0]) {
case TP_VARIANT_IBM:
case TP_VARIANT_ALPS:
case TP_VARIANT_ELAN:
case TP_VARIANT_NXP:
case TP_VARIANT_JYT_SYNAPTICS:
case TP_VARIANT_SYNAPTICS:
if (variant_id)
*variant_id = param[0];
if (firmware_id)
*firmware_id = param[1];
return 0;
}
return -ENODEV;
}
/*
* Write parameters to trackpad.
* in_power_on_state: Set to true if TP is in default / power-on state (ex. if
* power-on reset was run). If so, values will only be
* written to TP if they differ from power-on default.
*/
static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state)
{
struct trackpoint_data *tp = psmouse->private;
if (!in_power_on_state && tp->variant_id == TP_VARIANT_IBM) {
/*
* Disable features that may make device unusable
* with this driver.
*/
trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND,
TP_MASK_TWOHAND, TP_DEF_TWOHAND);
trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG,
TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG);
trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB,
TP_MASK_MB, TP_DEF_MB);
}
/*
* These properties can be changed in this driver. Only
* configure them if the values are non-default or if the TP is in
* an unknown state.
*/
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, drift_time);
/* toggles */
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback);
TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev);
return 0;
}
static void trackpoint_defaults(struct trackpoint_data *tp)
{
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, drift_time);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia);
/* toggles */
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback);
TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev);
}
static void trackpoint_disconnect(struct psmouse *psmouse)
{
device_remove_group(&psmouse->ps2dev.serio->dev,
&trackpoint_attr_group);
kfree(psmouse->private);
psmouse->private = NULL;
}
static int trackpoint_reconnect(struct psmouse *psmouse)
{
struct trackpoint_data *tp = psmouse->private;
int error;
bool was_reset;
error = trackpoint_start_protocol(psmouse, NULL, NULL);
if (error)
return error;
was_reset = tp->variant_id == TP_VARIANT_IBM &&
trackpoint_power_on_reset(&psmouse->ps2dev) == 0;
error = trackpoint_sync(psmouse, was_reset);
if (error)
return error;
return 0;
}
/* List of known incapable device PNP IDs */
static const char * const dt_incompatible_devices[] = {
"LEN0304",
"LEN0306",
"LEN0317",
"LEN031A",
"LEN031B",
"LEN031C",
"LEN031D",
};
/*
* Checks if it's a doubletap capable device.
* The PNP ID format is "PNP: LEN030d PNP0f13".
*/
static bool trackpoint_is_dt_capable(const char *pnp_id)
{
size_t i;
if (!pnp_id)
return false;
/* Must start with "PNP: LEN03" */
if (!strstarts(pnp_id, "PNP: LEN03"))
return false;
/* Ensure enough length before comparing */
if (strlen(pnp_id) < 12)
return false;
/* Check deny-list */
for (i = 0; i < ARRAY_SIZE(dt_incompatible_devices); i++) {
if (!strncmp(pnp_id + 5, dt_incompatible_devices[i], 7))
return false;
}
return true;
}
int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
struct trackpoint_data *tp;
u8 variant_id;
u8 firmware_id;
u8 button_info;
int error;
error = trackpoint_start_protocol(psmouse, &variant_id, &firmware_id);
if (error)
return error;
if (!set_properties)
return 0;
tp = kzalloc_obj(*tp);
if (!tp)
return -ENOMEM;
trackpoint_defaults(tp);
tp->variant_id = variant_id;
tp->firmware_id = firmware_id;
psmouse->private = tp;
psmouse->vendor = trackpoint_variants[variant_id];
psmouse->name = "TrackPoint";
psmouse->reconnect = trackpoint_reconnect;
psmouse->disconnect = trackpoint_disconnect;
if (variant_id != TP_VARIANT_IBM) {
/* Newer variants do not support extended button query. */
button_info = 0x33;
} else {
error = trackpoint_read(ps2dev, TP_EXT_BTN, &button_info);
if (error) {
psmouse_warn(psmouse,
"failed to get extended button data, assuming 3 buttons\n");
button_info = 0x33;
} else if (!button_info) {
psmouse_warn(psmouse,
"got 0 in extended button data, assuming 3 buttons\n");
button_info = 0x33;
}
}
if ((button_info & 0x0f) >= 3)
input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
__set_bit(INPUT_PROP_POINTER, psmouse->dev->propbit);
__set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit);
if (variant_id != TP_VARIANT_IBM ||
trackpoint_power_on_reset(ps2dev) != 0) {
/*
* Write defaults to TP if we did not reset the trackpoint.
*/
trackpoint_sync(psmouse, false);
}
error = device_add_group(&ps2dev->serio->dev, &trackpoint_attr_group);
if (error) {
psmouse_err(psmouse,
"failed to create sysfs attributes, error: %d\n",
error);
kfree(psmouse->private);
psmouse->private = NULL;
return -1;
}
psmouse_info(psmouse,
"%s TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
psmouse->vendor, firmware_id,
(button_info & 0xf0) >> 4, button_info & 0x0f);
if (trackpoint_is_dt_capable(ps2dev->serio->firmware_id)) {
error = trackpoint_write(ps2dev, TP_DOUBLETAP, TP_DOUBLETAP_ENABLE);
if (error)
psmouse_warn(psmouse, "Failed to enable doubletap: %d\n", error);
}
return 0;
}
@@ -0,0 +1,167 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* IBM TrackPoint PS/2 mouse driver
*
* Stephen Evanchik <evanchsa@gmail.com>
*/
#ifndef _TRACKPOINT_H
#define _TRACKPOINT_H
/*
* These constants are from the TrackPoint System
* Engineering documentation Version 4 from IBM Watson
* research:
* http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
*/
#define TP_COMMAND 0xE2 /* Commands start with this */
#define TP_READ_ID 0xE1 /* Sent for device identification */
/*
* Valid first byte responses to the "Read Secondary ID" (0xE1) command.
* 0x01 was the original IBM trackpoint, others implement very limited
* subset of trackpoint features.
*/
#define TP_VARIANT_IBM 0x01
#define TP_VARIANT_ALPS 0x02
#define TP_VARIANT_ELAN 0x03
#define TP_VARIANT_NXP 0x04
#define TP_VARIANT_JYT_SYNAPTICS 0x05
#define TP_VARIANT_SYNAPTICS 0x06
/*
* Commands
*/
#define TP_RECALIB 0x51 /* Recalibrate */
#define TP_POWER_DOWN 0x44 /* Can only be undone through HW reset */
#define TP_EXT_DEV 0x21 /* Determines if external device is connected (RO) */
#define TP_EXT_BTN 0x4B /* Read extended button status */
#define TP_POR 0x7F /* Execute Power on Reset */
#define TP_POR_RESULTS 0x25 /* Read Power on Self test results */
#define TP_DISABLE_EXT 0x40 /* Disable external pointing device */
#define TP_ENABLE_EXT 0x41 /* Enable external pointing device */
/*
* Mode manipulation
*/
#define TP_SET_SOFT_TRANS 0x4E /* Set mode */
#define TP_CANCEL_SOFT_TRANS 0xB9 /* Cancel mode */
#define TP_SET_HARD_TRANS 0x45 /* Mode can only be set */
/*
* Register oriented commands/properties
*/
#define TP_WRITE_MEM 0x81
#define TP_READ_MEM 0x80 /* Not used in this implementation */
/*
* RAM Locations for properties
*/
#define TP_SENS 0x4A /* Sensitivity */
#define TP_MB 0x4C /* Read Middle Button Status (RO) */
#define TP_INERTIA 0x4D /* Negative Inertia */
#define TP_SPEED 0x60 /* Speed of TP Cursor */
#define TP_REACH 0x57 /* Backup for Z-axis press */
#define TP_DRAGHYS 0x58 /* Drag Hysteresis */
/* (how hard it is to drag */
/* with Z-axis pressed) */
#define TP_DOUBLETAP 0x58 /* TrackPoint doubletap register */
#define TP_MINDRAG 0x59 /* Minimum amount of force needed */
/* to trigger dragging */
#define TP_THRESH 0x5C /* Minimum value for a Z-axis press */
#define TP_UP_THRESH 0x5A /* Used to generate a 'click' on Z-axis */
#define TP_Z_TIME 0x5E /* How sharp of a press */
#define TP_JENKS_CURV 0x5D /* Minimum curvature for double click */
#define TP_DRIFT_TIME 0x5F /* How long a 'hands off' condition */
/* must last (x*107ms) for drift */
/* correction to occur */
/*
* Toggling Flag bits
*/
#define TP_TOGGLE 0x47 /* Toggle command */
#define TP_TOGGLE_MB 0x23 /* Disable/Enable Middle Button */
#define TP_MASK_MB 0x01
#define TP_TOGGLE_EXT_DEV 0x23 /* Disable external device */
#define TP_MASK_EXT_DEV 0x02
#define TP_TOGGLE_DRIFT 0x23 /* Drift Correction */
#define TP_MASK_DRIFT 0x80
#define TP_TOGGLE_BURST 0x28 /* Burst Mode */
#define TP_MASK_BURST 0x80
#define TP_TOGGLE_PTSON 0x2C /* Press to Select */
#define TP_MASK_PTSON 0x01
#define TP_TOGGLE_HARD_TRANS 0x2C /* Alternate method to set Hard Transparency */
#define TP_MASK_HARD_TRANS 0x80
#define TP_TOGGLE_TWOHAND 0x2D /* Two handed */
#define TP_MASK_TWOHAND 0x01
#define TP_TOGGLE_STICKY_TWO 0x2D /* Sticky two handed */
#define TP_MASK_STICKY_TWO 0x04
#define TP_TOGGLE_SKIPBACK 0x2D /* Suppress movement after drag release */
#define TP_MASK_SKIPBACK 0x08
#define TP_TOGGLE_SOURCE_TAG 0x20 /* Bit 3 of the first packet will be set to
to the origin of the packet (external or TP) */
#define TP_MASK_SOURCE_TAG 0x80
#define TP_TOGGLE_EXT_TAG 0x22 /* Bit 3 of the first packet coming from the
external device will be forced to 1 */
#define TP_MASK_EXT_TAG 0x04
/* Doubletap register values */
#define TP_DOUBLETAP_ENABLE 0xFF /* Enable value */
#define TP_DOUBLETAP_DISABLE 0xFE /* Disable value */
/* Power on Self Test Results */
#define TP_POR_SUCCESS 0x3B
/*
* Default power on values
*/
#define TP_DEF_SENS 0x80
#define TP_DEF_INERTIA 0x06
#define TP_DEF_SPEED 0x61
#define TP_DEF_REACH 0x0A
#define TP_DEF_DRAGHYS 0xFF
#define TP_DEF_MINDRAG 0x14
#define TP_DEF_THRESH 0x08
#define TP_DEF_UP_THRESH 0xFF
#define TP_DEF_Z_TIME 0x26
#define TP_DEF_JENKS_CURV 0x87
#define TP_DEF_DRIFT_TIME 0x05
/* Toggles */
#define TP_DEF_MB 0x00
#define TP_DEF_PTSON 0x00
#define TP_DEF_SKIPBACK 0x00
#define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */
#define TP_DEF_TWOHAND 0x00
#define TP_DEF_SOURCE_TAG 0x00
#define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd))
struct trackpoint_data {
u8 variant_id;
u8 firmware_id;
u8 sensitivity, speed, inertia, reach;
u8 draghys, mindrag;
u8 thresh, upthresh;
u8 ztime, jenks;
u8 drift_time;
/* toggles */
bool press_to_select;
bool skipback;
bool ext_dev;
};
int trackpoint_detect(struct psmouse *psmouse, bool set_properties);
#endif /* _TRACKPOINT_H */
+468
View File
@@ -0,0 +1,468 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
*
* Copyright (C) 2014, VMware, Inc. All Rights Reserved.
*
* Twin device code is hugely inspired by the ALPS driver.
* Authors:
* Dmitry Torokhov <dmitry.torokhov@gmail.com>
* Thomas Hellstrom <thellstrom@vmware.com>
*/
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/hypervisor.h>
#include <asm/vmware.h>
#include "psmouse.h"
#include "vmmouse.h"
/*
* Main commands supported by the vmmouse hypervisor port.
*/
#define VMWARE_CMD_ABSPOINTER_DATA 39
#define VMWARE_CMD_ABSPOINTER_STATUS 40
#define VMWARE_CMD_ABSPOINTER_COMMAND 41
#define VMWARE_CMD_ABSPOINTER_RESTRICT 86
/*
* Subcommands for VMWARE_CMD_ABSPOINTER_COMMAND
*/
#define VMMOUSE_CMD_ENABLE 0x45414552U
#define VMMOUSE_CMD_DISABLE 0x000000f5U
#define VMMOUSE_CMD_REQUEST_RELATIVE 0x4c455252U
#define VMMOUSE_CMD_REQUEST_ABSOLUTE 0x53424152U
#define VMMOUSE_ERROR 0xffff0000U
#define VMMOUSE_VERSION_ID 0x3442554aU
#define VMMOUSE_RELATIVE_PACKET 0x00010000U
#define VMMOUSE_LEFT_BUTTON 0x20
#define VMMOUSE_RIGHT_BUTTON 0x10
#define VMMOUSE_MIDDLE_BUTTON 0x08
/*
* VMMouse Restrict command
*/
#define VMMOUSE_RESTRICT_ANY 0x00
#define VMMOUSE_RESTRICT_CPL0 0x01
#define VMMOUSE_RESTRICT_IOPL 0x02
#define VMMOUSE_MAX_X 0xFFFF
#define VMMOUSE_MAX_Y 0xFFFF
#define VMMOUSE_VENDOR "VMware"
#define VMMOUSE_NAME "VMMouse"
/**
* struct vmmouse_data - private data structure for the vmmouse driver
*
* @abs_dev: "Absolute" device used to report absolute mouse movement.
* @phys: Physical path for the absolute device.
* @dev_name: Name attribute name for the absolute device.
*/
struct vmmouse_data {
struct input_dev *abs_dev;
char phys[32];
char dev_name[128];
};
/**
* vmmouse_report_button - report button state on the correct input device
*
* @psmouse: Pointer to the psmouse struct
* @abs_dev: The absolute input device
* @rel_dev: The relative input device
* @pref_dev: The preferred device for reporting
* @code: Button code
* @value: Button value
*
* Report @value and @code on @pref_dev, unless the button is already
* pressed on the other device, in which case the state is reported on that
* device.
*/
static void vmmouse_report_button(struct psmouse *psmouse,
struct input_dev *abs_dev,
struct input_dev *rel_dev,
struct input_dev *pref_dev,
unsigned int code, int value)
{
if (test_bit(code, abs_dev->key))
pref_dev = abs_dev;
else if (test_bit(code, rel_dev->key))
pref_dev = rel_dev;
input_report_key(pref_dev, code, value);
}
/**
* vmmouse_report_events - process events on the vmmouse communications channel
*
* @psmouse: Pointer to the psmouse struct
*
* This function pulls events from the vmmouse communications channel and
* reports them on the correct (absolute or relative) input device. When the
* communications channel is drained, or if we've processed more than 255
* psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a
* host- or synchronization error, the function returns PSMOUSE_BAD_DATA in
* the hope that the caller will reset the communications channel.
*/
static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
{
struct input_dev *rel_dev = psmouse->dev;
struct vmmouse_data *priv = psmouse->private;
struct input_dev *abs_dev = priv->abs_dev;
struct input_dev *pref_dev;
u32 status, x, y, z;
unsigned int queue_length;
unsigned int count = 255;
while (count--) {
/* See if we have motion data. */
status = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_STATUS, 0);
if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
psmouse_err(psmouse, "failed to fetch status data\n");
/*
* After a few attempts this will result in
* reconnect.
*/
return PSMOUSE_BAD_DATA;
}
queue_length = status & 0xffff;
if (queue_length == 0)
break;
if (queue_length % 4) {
psmouse_err(psmouse, "invalid queue length\n");
return PSMOUSE_BAD_DATA;
}
/* Now get it */
status = vmware_hypercall4(VMWARE_CMD_ABSPOINTER_DATA, 4,
&x, &y, &z);
/*
* And report what we've got. Prefer to report button
* events on the same device where we report motion events.
* This doesn't work well with the mouse wheel, though. See
* below. Ideally we would want to report that on the
* preferred device as well.
*/
if (status & VMMOUSE_RELATIVE_PACKET) {
pref_dev = rel_dev;
input_report_rel(rel_dev, REL_X, (s32)x);
input_report_rel(rel_dev, REL_Y, -(s32)y);
} else {
pref_dev = abs_dev;
input_report_abs(abs_dev, ABS_X, x);
input_report_abs(abs_dev, ABS_Y, y);
}
/* Xorg seems to ignore wheel events on absolute devices */
input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z));
vmmouse_report_button(psmouse, abs_dev, rel_dev,
pref_dev, BTN_LEFT,
status & VMMOUSE_LEFT_BUTTON);
vmmouse_report_button(psmouse, abs_dev, rel_dev,
pref_dev, BTN_RIGHT,
status & VMMOUSE_RIGHT_BUTTON);
vmmouse_report_button(psmouse, abs_dev, rel_dev,
pref_dev, BTN_MIDDLE,
status & VMMOUSE_MIDDLE_BUTTON);
input_sync(abs_dev);
input_sync(rel_dev);
}
return PSMOUSE_FULL_PACKET;
}
/**
* vmmouse_process_byte - process data on the ps/2 channel
*
* @psmouse: Pointer to the psmouse struct
*
* When the ps/2 channel indicates that there is vmmouse data available,
* call vmmouse channel processing. Otherwise, continue to accept bytes. If
* there is a synchronization or communication data error, return
* PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse.
*/
static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
{
unsigned char *packet = psmouse->packet;
switch (psmouse->pktcnt) {
case 1:
return (packet[0] & 0x8) == 0x8 ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
case 2:
return PSMOUSE_GOOD_DATA;
default:
return vmmouse_report_events(psmouse);
}
}
/**
* vmmouse_disable - Disable vmmouse
*
* @psmouse: Pointer to the psmouse struct
*
* Tries to disable vmmouse mode.
*/
static void vmmouse_disable(struct psmouse *psmouse)
{
u32 status;
vmware_hypercall1(VMWARE_CMD_ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE);
status = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_STATUS, 0);
if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
psmouse_warn(psmouse, "failed to disable vmmouse device\n");
}
/**
* vmmouse_enable - Enable vmmouse and request absolute mode.
*
* @psmouse: Pointer to the psmouse struct
*
* Tries to enable vmmouse mode. Performs basic checks and requests
* absolute vmmouse mode.
* Returns 0 on success, -ENODEV on failure.
*/
static int vmmouse_enable(struct psmouse *psmouse)
{
u32 status, version;
/*
* Try enabling the device. If successful, we should be able to
* read valid version ID back from it.
*/
vmware_hypercall1(VMWARE_CMD_ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE);
/*
* See if version ID can be retrieved.
*/
status = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_STATUS, 0);
if ((status & 0x0000ffff) == 0) {
psmouse_dbg(psmouse, "empty flags - assuming no device\n");
return -ENXIO;
}
version = vmware_hypercall1(VMWARE_CMD_ABSPOINTER_DATA,
1 /* single item */);
if (version != VMMOUSE_VERSION_ID) {
psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
(unsigned) version, VMMOUSE_VERSION_ID);
vmmouse_disable(psmouse);
return -ENXIO;
}
/*
* Restrict ioport access, if possible.
*/
vmware_hypercall1(VMWARE_CMD_ABSPOINTER_RESTRICT,
VMMOUSE_RESTRICT_CPL0);
vmware_hypercall1(VMWARE_CMD_ABSPOINTER_COMMAND,
VMMOUSE_CMD_REQUEST_ABSOLUTE);
return 0;
}
/*
* Array of supported hypervisors.
*/
static enum x86_hypervisor_type vmmouse_supported_hypervisors[] = {
X86_HYPER_VMWARE,
X86_HYPER_KVM,
};
/**
* vmmouse_check_hypervisor - Check if we're running on a supported hypervisor
*/
static bool vmmouse_check_hypervisor(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++)
if (vmmouse_supported_hypervisors[i] == x86_hyper_type)
return true;
return false;
}
/**
* vmmouse_detect - Probe whether vmmouse is available
*
* @psmouse: Pointer to the psmouse struct
* @set_properties: Whether to set psmouse name and vendor
*
* Returns 0 if vmmouse channel is available. Negative error code if not.
*/
int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
{
u32 response, version, type;
if (!vmmouse_check_hypervisor()) {
psmouse_dbg(psmouse,
"VMMouse not running on supported hypervisor.\n");
return -ENXIO;
}
/* Check if the device is present */
response = ~VMWARE_HYPERVISOR_MAGIC;
version = vmware_hypercall3(VMWARE_CMD_GETVERSION, 0, &response, &type);
if (response != VMWARE_HYPERVISOR_MAGIC || version == 0xffffffffU)
return -ENXIO;
if (set_properties) {
psmouse->vendor = VMMOUSE_VENDOR;
psmouse->name = VMMOUSE_NAME;
psmouse->model = version;
}
return 0;
}
/**
* vmmouse_reset - Disable vmmouse and reset
*
* @psmouse: Pointer to the psmouse struct
*
* Tries to disable vmmouse mode before enter suspend.
*/
static void vmmouse_reset(struct psmouse *psmouse)
{
vmmouse_disable(psmouse);
psmouse_reset(psmouse);
}
/**
* vmmouse_disconnect - Take down vmmouse driver
*
* @psmouse: Pointer to the psmouse struct
*
* Takes down vmmouse driver and frees resources set up in vmmouse_init().
*/
static void vmmouse_disconnect(struct psmouse *psmouse)
{
struct vmmouse_data *priv = psmouse->private;
vmmouse_disable(psmouse);
psmouse_reset(psmouse);
input_unregister_device(priv->abs_dev);
kfree(priv);
}
/**
* vmmouse_reconnect - Reset the ps/2 - and vmmouse connections
*
* @psmouse: Pointer to the psmouse struct
*
* Attempts to reset the mouse connections. Returns 0 on success and
* -1 on failure.
*/
static int vmmouse_reconnect(struct psmouse *psmouse)
{
int error;
psmouse_reset(psmouse);
vmmouse_disable(psmouse);
error = vmmouse_enable(psmouse);
if (error) {
psmouse_err(psmouse,
"Unable to re-enable mouse when reconnecting, err: %d\n",
error);
return error;
}
return 0;
}
/**
* vmmouse_init - Initialize the vmmouse driver
*
* @psmouse: Pointer to the psmouse struct
*
* Requests the device and tries to enable vmmouse mode.
* If successful, sets up the input device for relative movement events.
* It also allocates another input device and sets it up for absolute motion
* events. Returns 0 on success and -1 on failure.
*/
int vmmouse_init(struct psmouse *psmouse)
{
struct vmmouse_data *priv;
struct input_dev *rel_dev = psmouse->dev, *abs_dev;
int error;
psmouse_reset(psmouse);
error = vmmouse_enable(psmouse);
if (error)
return error;
priv = kzalloc_obj(*priv);
abs_dev = input_allocate_device();
if (!priv || !abs_dev) {
error = -ENOMEM;
goto init_fail;
}
priv->abs_dev = abs_dev;
psmouse->private = priv;
/* Set up and register absolute device */
snprintf(priv->phys, sizeof(priv->phys), "%s/input1",
psmouse->ps2dev.serio->phys);
/* Mimic name setup for relative device in psmouse-base.c */
snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s",
VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME);
abs_dev->phys = priv->phys;
abs_dev->name = priv->dev_name;
abs_dev->id.bustype = BUS_I8042;
abs_dev->id.vendor = 0x0002;
abs_dev->id.product = PSMOUSE_VMMOUSE;
abs_dev->id.version = psmouse->model;
abs_dev->dev.parent = &psmouse->ps2dev.serio->dev;
/* Set absolute device capabilities */
input_set_capability(abs_dev, EV_KEY, BTN_LEFT);
input_set_capability(abs_dev, EV_KEY, BTN_RIGHT);
input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE);
input_set_capability(abs_dev, EV_ABS, ABS_X);
input_set_capability(abs_dev, EV_ABS, ABS_Y);
input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0);
input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0);
error = input_register_device(priv->abs_dev);
if (error)
goto init_fail;
/* Add wheel capability to the relative device */
input_set_capability(rel_dev, EV_REL, REL_WHEEL);
psmouse->protocol_handler = vmmouse_process_byte;
psmouse->disconnect = vmmouse_disconnect;
psmouse->reconnect = vmmouse_reconnect;
psmouse->cleanup = vmmouse_reset;
return 0;
init_fail:
vmmouse_disable(psmouse);
psmouse_reset(psmouse);
input_free_device(abs_dev);
kfree(priv);
psmouse->private = NULL;
return error;
}
+16
View File
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
*
* Copyright (C) 2014, VMware, Inc. All Rights Reserved.
*/
#ifndef _VMMOUSE_H
#define _VMMOUSE_H
#define VMMOUSE_PSNAME "VirtualPS/2"
int vmmouse_detect(struct psmouse *psmouse, bool set_properties);
int vmmouse_init(struct psmouse *psmouse);
#endif
+535
View File
@@ -0,0 +1,535 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers)
* DEC VSXXX-GA mouse (rectangular mouse, with ball)
* DEC VSXXX-AB tablet (digitizer with hair cross or stylus)
*
* Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
*
* The packet format was initially taken from a patch to GPM which is (C) 2001
* by Karsten Merker <merker@linuxtag.org>
* and Maciej W. Rozycki <macro@ds2.pg.gda.pl>
* Later on, I had access to the device's documentation (referenced below).
*/
/*
* Building an adaptor to DE9 / DB25 RS232
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for
* anything if you break your mouse, your computer or whatever!
*
* In theory, this mouse is a simple RS232 device. In practice, it has got
* a quite uncommon plug and the requirement to additionally get a power
* supply at +5V and -12V.
*
* If you look at the socket/jack (_not_ at the plug), we use this pin
* numbering:
* _______
* / 7 6 5 \
* | 4 --- 3 |
* \ 2 1 /
* -------
*
* DEC socket DE9 DB25 Note
* 1 (GND) 5 7 -
* 2 (RxD) 2 3 -
* 3 (TxD) 3 2 -
* 4 (-12V) - - Somewhere from the PSU. At ATX, it's
* the thin blue wire at pin 12 of the
* ATX power connector. Only required for
* VSXXX-AA/-GA mice.
* 5 (+5V) - - PSU (red wires of ATX power connector
* on pin 4, 6, 19 or 20) or HDD power
* connector (also red wire).
* 6 (+12V) - - HDD power connector, yellow wire. Only
* required for VSXXX-AB digitizer.
* 7 (dev. avail.) - - The mouse shorts this one to pin 1.
* This way, the host computer can detect
* the mouse. To use it with the adaptor,
* simply don't connect this pin.
*
* So to get a working adaptor, you need to connect the mouse with three
* wires to a RS232 port and two or three additional wires for +5V, +12V and
* -12V to the PSU.
*
* Flow specification for the link is 4800, 8o1.
*
* The mice and tablet are described in "VCB02 Video Subsystem - Technical
* Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine
* specific for DEC documentation. Try
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#undef VSXXXAA_DEBUG
#ifdef VSXXXAA_DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...) do {} while (0)
#endif
#define VSXXXAA_INTRO_MASK 0x80
#define VSXXXAA_INTRO_HEAD 0x80
#define IS_HDR_BYTE(x) \
(((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD)
#define VSXXXAA_PACKET_MASK 0xe0
#define VSXXXAA_PACKET_REL 0x80
#define VSXXXAA_PACKET_ABS 0xc0
#define VSXXXAA_PACKET_POR 0xa0
#define MATCH_PACKET_TYPE(data, type) \
(((data) & VSXXXAA_PACKET_MASK) == (type))
struct vsxxxaa {
struct input_dev *dev;
struct serio *serio;
#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */
unsigned char buf[BUFLEN];
unsigned char count;
unsigned char version;
unsigned char country;
unsigned char type;
char name[64];
char phys[32];
};
static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num)
{
if (num >= mouse->count) {
mouse->count = 0;
} else {
memmove(mouse->buf, mouse->buf + num, BUFLEN - num);
mouse->count -= num;
}
}
static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte)
{
if (mouse->count == BUFLEN) {
printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
mouse->name, mouse->phys);
vsxxxaa_drop_bytes(mouse, 1);
}
DBG(KERN_INFO "Queueing byte 0x%02x\n", byte);
mouse->buf[mouse->count++] = byte;
}
static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
{
switch (mouse->type) {
case 0x02:
strscpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
sizeof(mouse->name));
break;
case 0x04:
strscpy(mouse->name, "DEC VSXXX-AB digitizer",
sizeof(mouse->name));
break;
default:
snprintf(mouse->name, sizeof(mouse->name),
"unknown DEC pointer device (type = 0x%02x)",
mouse->type);
break;
}
printk(KERN_INFO
"Found %s version 0x%02x from country 0x%02x on port %s\n",
mouse->name, mouse->version, mouse->country, mouse->phys);
}
/*
* Returns number of bytes to be dropped, 0 if packet is okay.
*/
static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len)
{
int i;
/* First byte must be a header byte */
if (!IS_HDR_BYTE(mouse->buf[0])) {
DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
return 1;
}
/* Check all following bytes */
for (i = 1; i < packet_len; i++) {
if (IS_HDR_BYTE(mouse->buf[i])) {
printk(KERN_ERR
"Need to drop %d bytes of a broken packet.\n",
i - 1);
DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
packet_len, i, mouse->buf[i]);
return i - 1;
}
}
return 0;
}
static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse,
unsigned char type, size_t len)
{
return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type);
}
static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right;
int dx, dy;
/*
* Check for normal stream packets. This is three bytes,
* with the first byte's 3 MSB set to 100.
*
* [0]: 1 0 0 SignX SignY Left Middle Right
* [1]: 0 dx dx dx dx dx dx dx
* [2]: 0 dy dy dy dy dy dy dy
*/
/*
* Low 7 bit of byte 1 are abs(dx), bit 7 is
* 0, bit 4 of byte 0 is direction.
*/
dx = buf[1] & 0x7f;
dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1;
/*
* Low 7 bit of byte 2 are abs(dy), bit 7 is
* 0, bit 3 of byte 0 is direction.
*/
dy = buf[2] & 0x7f;
dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0.
*/
left = buf[0] & 0x04;
middle = buf[0] & 0x02;
right = buf[0] & 0x01;
vsxxxaa_drop_bytes(mouse, 3);
DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
mouse->name, mouse->phys, dx, dy,
left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r");
/*
* Report what we've found so far...
*/
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_MIDDLE, middle);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_TOUCH, 0);
input_report_rel(dev, REL_X, dx);
input_report_rel(dev, REL_Y, dy);
input_sync(dev);
}
static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right, touch;
int x, y;
/*
* Tablet position / button packet
*
* [0]: 1 1 0 B4 B3 B2 B1 Pr
* [1]: 0 0 X5 X4 X3 X2 X1 X0
* [2]: 0 0 X11 X10 X9 X8 X7 X6
* [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0
* [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6
*/
/*
* Get X/Y position. Y axis needs to be inverted since VSXXX-AB
* counts down->top while monitor counts top->bottom.
*/
x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
y = 1023 - y;
/*
* Get button state. It's bits <4..1> of byte 0.
*/
left = buf[0] & 0x02;
middle = buf[0] & 0x04;
right = buf[0] & 0x08;
touch = buf[0] & 0x10;
vsxxxaa_drop_bytes(mouse, 5);
DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
mouse->name, mouse->phys, x, y,
left ? "L" : "l", middle ? "M" : "m",
right ? "R" : "r", touch ? "T" : "t");
/*
* Report what we've found so far...
*/
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_MIDDLE, middle);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_TOUCH, touch);
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
input_sync(dev);
}
static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right;
unsigned char error;
/*
* Check for Power-On-Reset packets. These are sent out
* after plugging the mouse in, or when explicitly
* requested by sending 'T'.
*
* [0]: 1 0 1 0 R3 R2 R1 R0
* [1]: 0 M2 M1 M0 D3 D2 D1 D0
* [2]: 0 E6 E5 E4 E3 E2 E1 E0
* [3]: 0 0 0 0 0 Left Middle Right
*
* M: manufacturer location code
* R: revision code
* E: Error code. If it's in the range of 0x00..0x1f, only some
* minor problem occurred. Errors >= 0x20 are considered bad
* and the device may not work properly...
* D: <0010> == mouse, <0100> == tablet
*/
mouse->version = buf[0] & 0x0f;
mouse->country = (buf[1] >> 4) & 0x07;
mouse->type = buf[1] & 0x0f;
error = buf[2] & 0x7f;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0. Maybe even the bit <3>
* has some meaning if a tablet is attached.
*/
left = buf[0] & 0x04;
middle = buf[0] & 0x02;
right = buf[0] & 0x01;
vsxxxaa_drop_bytes(mouse, 4);
vsxxxaa_detection_done(mouse);
if (error <= 0x1f) {
/* No (serious) error. Report buttons */
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_MIDDLE, middle);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_TOUCH, 0);
input_sync(dev);
if (error != 0)
printk(KERN_INFO "Your %s on %s reports error=0x%02x\n",
mouse->name, mouse->phys, error);
}
/*
* If the mouse was hot-plugged, we need to force differential mode
* now... However, give it a second to recover from it's reset.
*/
printk(KERN_NOTICE
"%s on %s: Forcing standard packet format, "
"incremental streaming mode and 72 samples/sec\n",
mouse->name, mouse->phys);
serio_write(mouse->serio, 'S'); /* Standard format */
mdelay(50);
serio_write(mouse->serio, 'R'); /* Incremental */
mdelay(50);
serio_write(mouse->serio, 'L'); /* 72 samples/sec */
}
static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse)
{
unsigned char *buf = mouse->buf;
int stray_bytes;
/*
* Parse buffer to death...
*/
do {
/*
* Out of sync? Throw away what we don't understand. Each
* packet starts with a byte whose bit 7 is set. Unhandled
* packets (ie. which we don't know about or simply b0rk3d
* data...) will get shifted out of the buffer after some
* activity on the mouse.
*/
while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
printk(KERN_ERR "%s on %s: Dropping a byte to regain "
"sync with mouse data stream...\n",
mouse->name, mouse->phys);
vsxxxaa_drop_bytes(mouse, 1);
}
/*
* Check for packets we know about.
*/
if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet(mouse, 3);
if (!stray_bytes)
vsxxxaa_handle_REL_packet(mouse);
} else if (vsxxxaa_smells_like_packet(mouse,
VSXXXAA_PACKET_ABS, 5)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet(mouse, 5);
if (!stray_bytes)
vsxxxaa_handle_ABS_packet(mouse);
} else if (vsxxxaa_smells_like_packet(mouse,
VSXXXAA_PACKET_POR, 4)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet(mouse, 4);
if (!stray_bytes)
vsxxxaa_handle_POR_packet(mouse);
} else {
break; /* No REL, ABS or POR packet found */
}
if (stray_bytes > 0) {
printk(KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes(mouse, stray_bytes);
}
} while (1);
}
static irqreturn_t vsxxxaa_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct vsxxxaa *mouse = serio_get_drvdata(serio);
vsxxxaa_queue_byte(mouse, data);
vsxxxaa_parse_buffer(mouse);
return IRQ_HANDLED;
}
static void vsxxxaa_disconnect(struct serio *serio)
{
struct vsxxxaa *mouse = serio_get_drvdata(serio);
serio_close(serio);
serio_set_drvdata(serio, NULL);
input_unregister_device(mouse->dev);
kfree(mouse);
}
static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv)
{
struct vsxxxaa *mouse;
struct input_dev *input_dev;
int err = -ENOMEM;
mouse = kzalloc_obj(*mouse);
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
mouse->dev = input_dev;
mouse->serio = serio;
strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
sizeof(mouse->name));
snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys);
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->dev.parent = &serio->dev;
__set_bit(EV_KEY, input_dev->evbit); /* We have buttons */
__set_bit(EV_REL, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(BTN_LEFT, input_dev->keybit); /* We have 3 buttons */
__set_bit(BTN_MIDDLE, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit);
__set_bit(BTN_TOUCH, input_dev->keybit); /* ...and Tablet */
__set_bit(REL_X, input_dev->relbit);
__set_bit(REL_Y, input_dev->relbit);
input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
serio_set_drvdata(serio, mouse);
err = serio_open(serio, drv);
if (err)
goto fail2;
/*
* Request selftest. Standard packet format and differential
* mode will be requested after the device ID'ed successfully.
*/
serio_write(serio, 'T'); /* Test */
err = input_register_device(input_dev);
if (err)
goto fail3;
return 0;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
kfree(mouse);
return err;
}
static struct serio_device_id vsxxaa_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_VSXXXAA,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids);
static struct serio_driver vsxxxaa_drv = {
.driver = {
.name = "vsxxxaa",
},
.description = DRIVER_DESC,
.id_table = vsxxaa_serio_ids,
.connect = vsxxxaa_connect,
.interrupt = vsxxxaa_interrupt,
.disconnect = vsxxxaa_disconnect,
};
module_serio_driver(vsxxxaa_drv);