Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

406
drivers/input/mouse/Kconfig Normal file
View File

@@ -0,0 +1,406 @@
#
# 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 X86
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_LOGIPS2PP
bool "Logitech PS/2++ mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a Logictech 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_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.
Note that if you enable this driver you will need an updated
X.org Synaptics driver that does not require ABS_PRESSURE
reports from the touchpad (i.e. post 1.5.0 version). You can
grab a patch for the driver here:
http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch
If unsure, say N.
This driver exposes some configuration registers via sysfs
entries. For further information,
see <file:Documentation/input/elantech.txt>.
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_OLPC
bool "OLPC PS/2 mouse protocol extension"
depends on MOUSE_PS2 && OLPC
help
Say Y here if you have an OLPC XO-1 laptop (with built-in
PS/2 touchpad/tablet device). The manufacturer calls the
touchpad an HGPK.
If unsure, say N.
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/appletouch.txt>.
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/appletouch.txt>.
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
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_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on ISA
help
Say Y here if you have an InPort, Microsoft or ATI XL busmouse.
They are rather rare these days.
To compile this driver as a module, choose M here: the
module will be called inport.
config MOUSE_ATIXL
bool "ATI XL variant"
depends on MOUSE_INPORT
help
Say Y here if your mouse is of the ATI XL variety.
config MOUSE_LOGIBM
tristate "Logitech busmouse"
depends on ISA
help
Say Y here if you have a Logitech busmouse.
They are rather rare these days.
To compile this driver as a module, choose M here: the
module will be called logibm.
config MOUSE_PC110PAD
tristate "IBM PC110 touchpad"
depends on ISA
help
Say Y if you have the IBM PC-110 micro-notebook and want its
touchpad supported.
To compile this driver as a module, choose M here: the
module will be called pc110pad.
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. Theses 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
select INPUT_POLLDEV
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_PXA930_TRKBALL
tristate "PXA930 Trackball mouse"
depends on CPU_PXA930 || CPU_PXA935
help
Say Y here to support PXA930 Trackball 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.
config MOUSE_NAVPOINT_PXA27x
tristate "Synaptics NavPoint (PXA27x SSP/SPI)"
depends on PXA27x && PXA_SSP
help
This driver adds support for the Synaptics NavPoint touchpad connected
to a PXA27x SSP port in SPI slave mode. The device emulates a mouse;
a tap or tap-and-a-half drag gesture emulates the left mouse button.
For example, use the xf86-input-evdev driver for an X pointing device.
To compile this driver as a module, choose M here: the
module will be called navpoint.
endif

View File

@@ -0,0 +1,41 @@
#
# 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) += cyapa.o
obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.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
psmouse-objs := psmouse-base.o synaptics.o
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.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
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

1844
drivers/input/mouse/alps.c Normal file

File diff suppressed because it is too large Load Diff

176
drivers/input/mouse/alps.h Normal file
View File

@@ -0,0 +1,176 @@
/*
* ALPS touchpad PS/2 mouse driver
*
* Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _ALPS_H
#define _ALPS_H
#define ALPS_PROTO_V1 1
#define ALPS_PROTO_V2 2
#define ALPS_PROTO_V3 3
#define ALPS_PROTO_V4 4
#define ALPS_PROTO_V5 5
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
* @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
* (aka command mode response) identifies the firmware minor version. This
* can be used to distinguish different hardware models which are not
* uniquely identifiable through their E7 responses.
* @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.
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
*
* 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 {
unsigned char signature[3];
unsigned char command_mode_resp;
unsigned char proto_version;
unsigned char byte0, mask0;
unsigned char flags;
};
/**
* 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_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.
* @x: X position for ST.
* @y: Y position for ST.
* @z: Z position for ST.
* @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;
unsigned int x;
unsigned int y;
unsigned int z;
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
* @dev2: "Relative" device used to report trackstick or mouse activity.
* @phys: Physical path for the relative device.
* @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.
* @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.
* @x1: First X coordinate from last MT report.
* @x2: Second X coordinate from last MT report.
* @y1: First Y coordinate from last MT report.
* @y2: Second Y coordinate from last MT report.
* @fingers: Number of fingers from last MT report.
* @quirks: Bitmap of ALPS_QUIRK_*.
* @timer: Timer for flushing out the final report packet in the stream.
*/
struct alps_data {
struct input_dev *dev2;
char phys[32];
/* these are autodetected when the device is identified */
const struct alps_nibble_commands *nibble_commands;
int addr_command;
unsigned char proto_version;
unsigned char byte0, mask0;
unsigned char flags;
int x_max;
int y_max;
int x_bits;
int y_bits;
int (*hw_init)(struct psmouse *psmouse);
void (*process_packet)(struct psmouse *psmouse);
void (*decode_fields)(struct alps_fields *f, unsigned char *p);
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
int prev_fin;
int multi_packet;
unsigned char multi_data[6];
int x1, x2, y1, y2;
int fingers;
u8 quirks;
struct timer_list timer;
};
#define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */
#ifdef CONFIG_MOUSE_PS2_ALPS
int alps_detect(struct psmouse *psmouse, bool set_properties);
int alps_init(struct psmouse *psmouse);
#else
inline int alps_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
inline int alps_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_ALPS */
#endif

View File

@@ -0,0 +1,151 @@
/*
* 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
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation
*/
#include <linux/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 <asm/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 int __exit amimouse_remove(struct platform_device *pdev)
{
struct input_dev *dev = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
input_unregister_device(dev);
return 0;
}
static struct platform_driver amimouse_driver = {
.remove = __exit_p(amimouse_remove),
.driver = {
.name = "amiga-mouse",
.owner = THIS_MODULE,
},
};
module_platform_driver_probe(amimouse_driver, amimouse_probe);
MODULE_ALIAS("platform:amiga-mouse");

View File

@@ -0,0 +1,948 @@
/*
* 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.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.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 */
};
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,
};
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,
};
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,
};
static const struct atp_info geyser3_info = {
.xsensors = 20,
.ysensors = 10,
.xfact = 64,
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
};
static const struct atp_info geyser4_info = {
.xsensors = 20,
.ysensors = 10,
.xfact = 64,
.yfact = 64,
.datalen = 64,
.callback = atp_complete_geyser_3_4,
};
#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 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
/* amount of fuzz this touchpad generates */
#define ATP_FUZZ 16
/* 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
/* 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 open;
bool valid; /* are the samples valid? */
bool size_detect_done;
bool overflow_warned;
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 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(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
int i;
/* 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;
continue;
}
/*
* 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>
*/
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;
}
/*
* Subtracts threshold so a high sensor that just passes the
* threshold won't skew the calculated absolute coordinate.
* Fixes an issue where slowly moving the mouse would
* occasionally jump a number of pixels (slowly moving the
* finger makes this issue most apparent.)
*/
pcum += (xy_sensors[i] - threshold) * i;
psum += (xy_sensors[i] - threshold);
}
if (psum > 0) {
*z = psum;
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;
}
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,
ATP_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;
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 = 1;
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->xy_acc, ATP_XSENSORS,
dev->info->xfact, &x_z, &x_f);
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
if (x && y) {
if (dev->x_old != -1) {
x = (dev->x_old * 3 + x) >> 2;
y = (dev->y_old * 3 + y) >> 2;
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, max(x_f, y_f));
}
dev->x_old = x;
dev->y_old = y;
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
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));
}
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;
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->xy_acc, ATP_XSENSORS,
dev->info->xfact, &x_z, &x_f);
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
dev->info->yfact, &y_z, &y_f);
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
if (x && y) {
if (dev->x_old != -1) {
x = (dev->x_old * 3 + x) >> 2;
y = (dev->y_old * 3 + y) >> 2;
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, max(x_f, y_f));
}
dev->x_old = x;
dev->y_old = y;
} else if (!x && !y) {
dev->x_old = dev->y_old = -1;
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));
}
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_ATOMIC))
return -EIO;
dev->open = 1;
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);
dev->open = 0;
}
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_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_in_endpointAddr = 0;
int i, error = -ENOMEM;
const struct atp_info *info = (const struct atp_info *)id->driver_info;
/* set up the endpoint information */
/* use only the first interrupt-in endpoint */
iface_desc = iface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
break;
}
}
if (!int_in_endpointAddr) {
dev_err(&iface->dev, "Could not find int-in endpoint\n");
return -EIO;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
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, int_in_endpointAddr),
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,
ATP_FUZZ, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
(dev->info->ysensors - 1) * dev->info->yfact - 1,
ATP_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);
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);
INIT_WORK(&dev->work, atp_reinit);
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;
if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
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);
if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
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);

View File

@@ -0,0 +1,158 @@
/*
* 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>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <asm/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);

View File

@@ -0,0 +1,984 @@
/*
* Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver
*
* Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
*
* The USB initialization and package decoding was made by
* Scott Shawcroft as part of the touchd user-space driver project:
* Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
*
* The BCM5974 driver is based on the appletouch driver:
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
* Copyright (C) 2005 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)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/mutex.h>
#include <linux/input/mt.h>
#define USB_VENDOR_ID_APPLE 0x05ac
/* MacbookAir, aka wellspring */
#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223
#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224
#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225
/* MacbookProPenryn, aka wellspring2 */
#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
/* Macbook5,1 (unibody), aka wellspring3 */
#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236
#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237
#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
/* MacbookAir3,2 (unibody), aka wellspring5 */
#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f
#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240
#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241
/* MacbookAir3,1 (unibody), aka wellspring4 */
#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242
#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243
#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244
/* Macbook8 (unibody, March 2011) */
#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245
#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246
#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247
/* MacbookAir4,1 (unibody, July 2011) */
#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249
#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a
#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b
/* MacbookAir4,2 (unibody, July 2011) */
#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c
#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d
#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e
/* Macbook8,2 (unibody) */
#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252
#define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253
#define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254
/* MacbookPro10,1 (unibody, June 2012) */
#define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI 0x0262
#define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO 0x0263
#define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS 0x0264
/* MacbookPro10,2 (unibody, October 2012) */
#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259
#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a
#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b
/* MacbookAir6,2 (unibody, June 2013) */
#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
#define BCM5974_DEVICE(prod) { \
.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL), \
.idVendor = USB_VENDOR_ID_APPLE, \
.idProduct = (prod), \
.bInterfaceClass = USB_INTERFACE_CLASS_HID, \
.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \
}
/* table of devices that work with this driver */
static const struct usb_device_id bcm5974_table[] = {
/* MacbookAir1.1 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
/* MacbookProPenryn */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
/* Macbook5,1 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
/* MacbookAir3,2 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
/* MacbookAir3,1 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
/* MacbookPro8 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
/* MacbookAir4,1 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
/* MacbookAir4,2 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
/* MacbookPro8,2 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
/* MacbookPro10,1 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_JIS),
/* MacbookPro10,2 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS),
/* MacbookAir6,2 */
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),
/* Terminating entry */
{}
};
MODULE_DEVICE_TABLE(usb, bcm5974_table);
MODULE_AUTHOR("Henrik Rydberg");
MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
MODULE_LICENSE("GPL");
#define dprintk(level, format, a...)\
{ if (debug >= level) printk(KERN_DEBUG format, ##a); }
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");
/* button data structure */
struct bt_data {
u8 unknown1; /* constant */
u8 button; /* left button */
u8 rel_x; /* relative x coordinate */
u8 rel_y; /* relative y coordinate */
};
/* trackpad header types */
enum tp_type {
TYPE1, /* plain trackpad */
TYPE2, /* button integrated in trackpad */
TYPE3 /* additional header fields since June 2013 */
};
/* trackpad finger data offsets, le16-aligned */
#define FINGER_TYPE1 (13 * sizeof(__le16))
#define FINGER_TYPE2 (15 * sizeof(__le16))
#define FINGER_TYPE3 (19 * sizeof(__le16))
/* trackpad button data offsets */
#define BUTTON_TYPE2 15
#define BUTTON_TYPE3 23
/* list of device capability bits */
#define HAS_INTEGRATED_BUTTON 1
/* trackpad finger structure, le16-aligned */
struct tp_finger {
__le16 origin; /* zero when switching track finger */
__le16 abs_x; /* absolute x coodinate */
__le16 abs_y; /* absolute y coodinate */
__le16 rel_x; /* relative x coodinate */
__le16 rel_y; /* relative y coodinate */
__le16 tool_major; /* tool area, major axis */
__le16 tool_minor; /* tool area, minor axis */
__le16 orientation; /* 16384 when point, else 15 bit angle */
__le16 touch_major; /* touch area, major axis */
__le16 touch_minor; /* touch area, minor axis */
__le16 unused[3]; /* zeros */
__le16 multi; /* one finger: varies, more fingers: constant */
} __attribute__((packed,aligned(2)));
/* trackpad finger data size, empirically at least ten fingers */
#define MAX_FINGERS 16
#define SIZEOF_FINGER sizeof(struct tp_finger)
#define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER)
#define MAX_FINGER_ORIENTATION 16384
/* device-specific parameters */
struct bcm5974_param {
int snratio; /* signal-to-noise ratio */
int min; /* device minimum reading */
int max; /* device maximum reading */
};
/* device-specific configuration */
struct bcm5974_config {
int ansi, iso, jis; /* the product id of this device */
int caps; /* device capability bitmask */
int bt_ep; /* the endpoint of the button interface */
int bt_datalen; /* data length of the button interface */
int tp_ep; /* the endpoint of the trackpad interface */
enum tp_type tp_type; /* type of trackpad interface */
int tp_offset; /* offset to trackpad finger data */
int tp_datalen; /* data length of the trackpad interface */
struct bcm5974_param p; /* finger pressure limits */
struct bcm5974_param w; /* finger width limits */
struct bcm5974_param x; /* horizontal limits */
struct bcm5974_param y; /* vertical limits */
struct bcm5974_param o; /* orientation limits */
};
/* logical device structure */
struct bcm5974 {
char phys[64];
struct usb_device *udev; /* usb device */
struct usb_interface *intf; /* our interface */
struct input_dev *input; /* input dev */
struct bcm5974_config cfg; /* device configuration */
struct mutex pm_mutex; /* serialize access to open/suspend */
int opened; /* 1: opened, 0: closed */
struct urb *bt_urb; /* button usb request block */
struct bt_data *bt_data; /* button transferred data */
struct urb *tp_urb; /* trackpad usb request block */
u8 *tp_data; /* trackpad transferred data */
const struct tp_finger *index[MAX_FINGERS]; /* finger index data */
struct input_mt_pos pos[MAX_FINGERS]; /* position array */
int slots[MAX_FINGERS]; /* slot assignments */
};
/* logical signal quality */
#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
#define SN_WIDTH 25 /* width signal-to-noise ratio */
#define SN_COORD 250 /* coordinate signal-to-noise ratio */
#define SN_ORIENT 10 /* orientation signal-to-noise ratio */
/* device constants */
static const struct bcm5974_config bcm5974_config_table[] = {
{
USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
0,
0x84, sizeof(struct bt_data),
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 256 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4824, 5342 },
{ SN_COORD, -172, 5820 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
0,
0x84, sizeof(struct bt_data),
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 256 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4824, 4824 },
{ SN_COORD, -172, 4290 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING3_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING3_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4460, 5166 },
{ SN_COORD, -75, 6700 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING4_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING4_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4620, 5140 },
{ SN_COORD, -150, 6600 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4616, 5112 },
{ SN_COORD, -142, 5234 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING5_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING5_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4415, 5050 },
{ SN_COORD, -55, 6680 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING6_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING6_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4620, 5140 },
{ SN_COORD, -150, 6600 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4750, 5280 },
{ SN_COORD, -150, 6730 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4620, 5140 },
{ SN_COORD, -150, 6600 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING7_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING7_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4750, 5280 },
{ SN_COORD, -150, 6730 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS,
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4750, 5280 },
{ SN_COORD, -150, 6730 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI,
USB_DEVICE_ID_APPLE_WELLSPRING8_ISO,
USB_DEVICE_ID_APPLE_WELLSPRING8_JIS,
HAS_INTEGRATED_BUTTON,
0, sizeof(struct bt_data),
0x83, TYPE3, FINGER_TYPE3, FINGER_TYPE3 + SIZEOF_ALL_FINGERS,
{ SN_PRESSURE, 0, 300 },
{ SN_WIDTH, 0, 2048 },
{ SN_COORD, -4620, 5140 },
{ SN_COORD, -150, 6600 },
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{}
};
/* return the device-specific configuration by device */
static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
{
u16 id = le16_to_cpu(udev->descriptor.idProduct);
const struct bcm5974_config *cfg;
for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
return cfg;
return bcm5974_config_table;
}
/* convert 16-bit little endian to signed integer */
static inline int raw2int(__le16 x)
{
return (signed short)le16_to_cpu(x);
}
static void set_abs(struct input_dev *input, unsigned int code,
const struct bcm5974_param *p)
{
int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
}
/* setup which logical events to report */
static void setup_events_to_report(struct input_dev *input_dev,
const struct bcm5974_config *cfg)
{
__set_bit(EV_ABS, input_dev->evbit);
/* for synaptics only */
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
/* finger touch area */
set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
/* finger approach area */
set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
/* finger orientation */
set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
/* finger position */
set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_LEFT, input_dev->keybit);
if (cfg->caps & HAS_INTEGRATED_BUTTON)
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
input_mt_init_slots(input_dev, MAX_FINGERS,
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
}
/* report button data as logical button state */
static int report_bt_state(struct bcm5974 *dev, int size)
{
if (size != sizeof(struct bt_data))
return -EIO;
dprintk(7,
"bcm5974: button data: %x %x %x %x\n",
dev->bt_data->unknown1, dev->bt_data->button,
dev->bt_data->rel_x, dev->bt_data->rel_y);
input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
input_sync(dev->input);
return 0;
}
static void report_finger_data(struct input_dev *input, int slot,
const struct input_mt_pos *pos,
const struct tp_finger *f)
{
input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
raw2int(f->touch_major) << 1);
input_report_abs(input, ABS_MT_TOUCH_MINOR,
raw2int(f->touch_minor) << 1);
input_report_abs(input, ABS_MT_WIDTH_MAJOR,
raw2int(f->tool_major) << 1);
input_report_abs(input, ABS_MT_WIDTH_MINOR,
raw2int(f->tool_minor) << 1);
input_report_abs(input, ABS_MT_ORIENTATION,
MAX_FINGER_ORIENTATION - raw2int(f->orientation));
input_report_abs(input, ABS_MT_POSITION_X, pos->x);
input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
}
static void report_synaptics_data(struct input_dev *input,
const struct bcm5974_config *cfg,
const struct tp_finger *f, int raw_n)
{
int abs_p = 0, abs_w = 0;
if (raw_n) {
int p = raw2int(f->touch_major);
int w = raw2int(f->tool_major);
if (p > 0 && raw2int(f->origin)) {
abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
}
}
input_report_abs(input, ABS_PRESSURE, abs_p);
input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
}
/* report trackpad data as logical trackpad state */
static int report_tp_state(struct bcm5974 *dev, int size)
{
const struct bcm5974_config *c = &dev->cfg;
const struct tp_finger *f;
struct input_dev *input = dev->input;
int raw_n, i, n = 0;
if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
return -EIO;
/* finger data, le16-aligned */
f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
for (i = 0; i < raw_n; i++) {
if (raw2int(f[i].touch_major) == 0)
continue;
dev->pos[n].x = raw2int(f[i].abs_x);
dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y);
dev->index[n++] = &f[i];
}
input_mt_assign_slots(input, dev->slots, dev->pos, n);
for (i = 0; i < n; i++)
report_finger_data(input, dev->slots[i],
&dev->pos[i], dev->index[i]);
input_mt_sync_frame(input);
report_synaptics_data(input, c, f, raw_n);
/* type 2 reports button events via ibt only */
if (c->tp_type == TYPE2) {
int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
input_report_key(input, BTN_LEFT, ibt);
}
if (c->tp_type == TYPE3)
input_report_key(input, BTN_LEFT, dev->tp_data[BUTTON_TYPE3]);
input_sync(input);
return 0;
}
/* Wellspring initialization constants */
#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1
#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9
#define BCM5974_WELLSPRING_MODE_REQUEST_VALUE 0x300
#define BCM5974_WELLSPRING_MODE_REQUEST_INDEX 0
#define BCM5974_WELLSPRING_MODE_VENDOR_VALUE 0x01
#define BCM5974_WELLSPRING_MODE_NORMAL_VALUE 0x08
static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
{
int retval = 0, size;
char *data;
/* Type 3 does not require a mode switch */
if (dev->cfg.tp_type == TYPE3)
return 0;
data = kmalloc(8, GFP_KERNEL);
if (!data) {
dev_err(&dev->intf->dev, "out of memory\n");
retval = -ENOMEM;
goto out;
}
/* read configuration */
size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
if (size != 8) {
dev_err(&dev->intf->dev, "could not read from device\n");
retval = -EIO;
goto out;
}
/* apply the mode switch */
data[0] = on ?
BCM5974_WELLSPRING_MODE_VENDOR_VALUE :
BCM5974_WELLSPRING_MODE_NORMAL_VALUE;
/* write configuration */
size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
if (size != 8) {
dev_err(&dev->intf->dev, "could not write to device\n");
retval = -EIO;
goto out;
}
dprintk(2, "bcm5974: switched to %s mode.\n",
on ? "wellspring" : "normal");
out:
kfree(data);
return retval;
}
static void bcm5974_irq_button(struct urb *urb)
{
struct bcm5974 *dev = urb->context;
struct usb_interface *intf = dev->intf;
int error;
switch (urb->status) {
case 0:
break;
case -EOVERFLOW:
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
dev_dbg(&intf->dev, "button urb shutting down: %d\n",
urb->status);
return;
default:
dev_dbg(&intf->dev, "button urb status: %d\n", urb->status);
goto exit;
}
if (report_bt_state(dev, dev->bt_urb->actual_length))
dprintk(1, "bcm5974: bad button package, length: %d\n",
dev->bt_urb->actual_length);
exit:
error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
if (error)
dev_err(&intf->dev, "button urb failed: %d\n", error);
}
static void bcm5974_irq_trackpad(struct urb *urb)
{
struct bcm5974 *dev = urb->context;
struct usb_interface *intf = dev->intf;
int error;
switch (urb->status) {
case 0:
break;
case -EOVERFLOW:
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
dev_dbg(&intf->dev, "trackpad urb shutting down: %d\n",
urb->status);
return;
default:
dev_dbg(&intf->dev, "trackpad urb status: %d\n", urb->status);
goto exit;
}
/* control response ignored */
if (dev->tp_urb->actual_length == 2)
goto exit;
if (report_tp_state(dev, dev->tp_urb->actual_length))
dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
dev->tp_urb->actual_length);
exit:
error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
if (error)
dev_err(&intf->dev, "trackpad urb failed: %d\n", error);
}
/*
* The Wellspring trackpad, like many recent Apple trackpads, share
* the usb device with the keyboard. Since keyboards are usually
* handled by the HID system, the device ends up being handled by two
* modules. Setting up the device therefore becomes slightly
* complicated. To enable multitouch features, a mode switch is
* required, which is usually applied via the control interface of the
* device. It can be argued where this switch should take place. In
* some drivers, like appletouch, the switch is made during
* probe. However, the hid module may also alter the state of the
* device, resulting in trackpad malfunction under certain
* circumstances. To get around this problem, there is at least one
* example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
* receive a reset_resume request rather than the normal resume.
* Since the implementation of reset_resume is equal to mode switch
* plus start_traffic, it seems easier to always do the switch when
* starting traffic on the device.
*/
static int bcm5974_start_traffic(struct bcm5974 *dev)
{
int error;
error = bcm5974_wellspring_mode(dev, true);
if (error) {
dprintk(1, "bcm5974: mode switch failed\n");
goto err_out;
}
if (dev->bt_urb) {
error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
if (error)
goto err_reset_mode;
}
error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
if (error)
goto err_kill_bt;
return 0;
err_kill_bt:
usb_kill_urb(dev->bt_urb);
err_reset_mode:
bcm5974_wellspring_mode(dev, false);
err_out:
return error;
}
static void bcm5974_pause_traffic(struct bcm5974 *dev)
{
usb_kill_urb(dev->tp_urb);
usb_kill_urb(dev->bt_urb);
bcm5974_wellspring_mode(dev, false);
}
/*
* The code below implements open/close and manual suspend/resume.
* All functions may be called in random order.
*
* Opening a suspended device fails with EACCES - permission denied.
*
* Failing a resume leaves the device resumed but closed.
*/
static int bcm5974_open(struct input_dev *input)
{
struct bcm5974 *dev = input_get_drvdata(input);
int error;
error = usb_autopm_get_interface(dev->intf);
if (error)
return error;
mutex_lock(&dev->pm_mutex);
error = bcm5974_start_traffic(dev);
if (!error)
dev->opened = 1;
mutex_unlock(&dev->pm_mutex);
if (error)
usb_autopm_put_interface(dev->intf);
return error;
}
static void bcm5974_close(struct input_dev *input)
{
struct bcm5974 *dev = input_get_drvdata(input);
mutex_lock(&dev->pm_mutex);
bcm5974_pause_traffic(dev);
dev->opened = 0;
mutex_unlock(&dev->pm_mutex);
usb_autopm_put_interface(dev->intf);
}
static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
{
struct bcm5974 *dev = usb_get_intfdata(iface);
mutex_lock(&dev->pm_mutex);
if (dev->opened)
bcm5974_pause_traffic(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static int bcm5974_resume(struct usb_interface *iface)
{
struct bcm5974 *dev = usb_get_intfdata(iface);
int error = 0;
mutex_lock(&dev->pm_mutex);
if (dev->opened)
error = bcm5974_start_traffic(dev);
mutex_unlock(&dev->pm_mutex);
return error;
}
static int bcm5974_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(iface);
const struct bcm5974_config *cfg;
struct bcm5974 *dev;
struct input_dev *input_dev;
int error = -ENOMEM;
/* find the product index */
cfg = bcm5974_get_config(udev);
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
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->cfg = *cfg;
mutex_init(&dev->pm_mutex);
/* setup urbs */
if (cfg->tp_type == TYPE1) {
dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->bt_urb)
goto err_free_devs;
}
dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->tp_urb)
goto err_free_bt_urb;
if (dev->bt_urb) {
dev->bt_data = usb_alloc_coherent(dev->udev,
dev->cfg.bt_datalen, GFP_KERNEL,
&dev->bt_urb->transfer_dma);
if (!dev->bt_data)
goto err_free_urb;
}
dev->tp_data = usb_alloc_coherent(dev->udev,
dev->cfg.tp_datalen, GFP_KERNEL,
&dev->tp_urb->transfer_dma);
if (!dev->tp_data)
goto err_free_bt_buffer;
if (dev->bt_urb)
usb_fill_int_urb(dev->bt_urb, udev,
usb_rcvintpipe(udev, cfg->bt_ep),
dev->bt_data, dev->cfg.bt_datalen,
bcm5974_irq_button, dev, 1);
usb_fill_int_urb(dev->tp_urb, udev,
usb_rcvintpipe(udev, cfg->tp_ep),
dev->tp_data, dev->cfg.tp_datalen,
bcm5974_irq_trackpad, dev, 1);
/* create bcm5974 device */
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
input_dev->name = "bcm5974";
input_dev->phys = dev->phys;
usb_to_input_id(dev->udev, &input_dev->id);
/* report driver capabilities via the version field */
input_dev->id.version = cfg->caps;
input_dev->dev.parent = &iface->dev;
input_set_drvdata(input_dev, dev);
input_dev->open = bcm5974_open;
input_dev->close = bcm5974_close;
setup_events_to_report(input_dev, cfg);
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->cfg.tp_datalen,
dev->tp_data, dev->tp_urb->transfer_dma);
err_free_bt_buffer:
if (dev->bt_urb)
usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
dev->bt_data, dev->bt_urb->transfer_dma);
err_free_urb:
usb_free_urb(dev->tp_urb);
err_free_bt_urb:
usb_free_urb(dev->bt_urb);
err_free_devs:
usb_set_intfdata(iface, NULL);
input_free_device(input_dev);
kfree(dev);
return error;
}
static void bcm5974_disconnect(struct usb_interface *iface)
{
struct bcm5974 *dev = usb_get_intfdata(iface);
usb_set_intfdata(iface, NULL);
input_unregister_device(dev->input);
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
dev->tp_data, dev->tp_urb->transfer_dma);
if (dev->bt_urb)
usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
dev->bt_data, dev->bt_urb->transfer_dma);
usb_free_urb(dev->tp_urb);
usb_free_urb(dev->bt_urb);
kfree(dev);
}
static struct usb_driver bcm5974_driver = {
.name = "bcm5974",
.probe = bcm5974_probe,
.disconnect = bcm5974_disconnect,
.suspend = bcm5974_suspend,
.resume = bcm5974_resume,
.id_table = bcm5974_table,
.supports_autosuspend = 1,
};
module_usb_driver(bcm5974_driver);

2375
drivers/input/mouse/cyapa.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,732 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/init.h>
#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 unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
psmouse_dbg(psmouse,
"sending command 0x%02x failed, resp 0x%02x\n",
value & 0xff, ps2dev->nak);
if (ps2dev->nak == CYTP_PS2_RETRY)
return CYTP_PS2_RETRY;
else
return CYTP_PS2_ERROR;
}
#ifdef CYTP_DEBUG_VERBOSE
psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
value & 0xff);
#endif
return 0;
}
static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
unsigned char data)
{
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, cmd & 0xff);
if (rc == CYTP_PS2_RETRY) {
rc = cypress_ps2_sendbyte(psmouse, 0x00);
if (rc == CYTP_PS2_RETRY)
rc = cypress_ps2_sendbyte(psmouse, 0x0a);
}
if (rc == CYTP_PS2_ERROR)
continue;
rc = cypress_ps2_sendbyte(psmouse, data);
if (rc == CYTP_PS2_RETRY)
rc = cypress_ps2_sendbyte(psmouse, data);
if (rc == CYTP_PS2_ERROR)
continue;
else
break;
} while (--tries > 0);
ps2_end_command(ps2dev);
return rc;
}
static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
unsigned char cmd,
unsigned char *param)
{
int rc;
struct ps2dev *ps2dev = &psmouse->ps2dev;
enum psmouse_state old_state;
int pktsize;
ps2_begin_command(&psmouse->ps2dev);
old_state = psmouse->state;
psmouse->state = PSMOUSE_CMD_MODE;
psmouse->pktcnt = 0;
pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
memset(param, 0, pktsize);
rc = cypress_ps2_sendbyte(psmouse, 0xe9);
if (rc < 0)
goto out;
wait_event_timeout(ps2dev->wait,
(psmouse->pktcnt >= pktsize),
msecs_to_jiffies(CYTP_CMD_TIMEOUT));
memcpy(param, psmouse->packet, pktsize);
psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
cmd, pktsize, param);
out:
psmouse->state = old_state;
psmouse->pktcnt = 0;
ps2_end_command(&psmouse->ps2dev);
return rc;
}
static bool cypress_verify_cmd_state(struct psmouse *psmouse,
unsigned char cmd, unsigned char *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, unsigned char cmd,
unsigned char *param)
{
int tries = CYTP_PS2_CMD_TRIES;
int rc;
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,
PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
cypress_ps2_ext_cmd(psmouse,
PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
cypress_ps2_ext_cmd(psmouse,
PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
cypress_ps2_ext_cmd(psmouse,
PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
if (rc)
continue;
if (cypress_verify_cmd_state(psmouse, cmd, param))
return 0;
} while (--tries > 0);
return -EIO;
}
int cypress_detect(struct psmouse *psmouse, bool set_properties)
{
unsigned char 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;
unsigned char 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;
unsigned char 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 ret;
ret = cypress_read_fw_version(psmouse);
if (ret)
return ret;
ret = cypress_read_tp_metrics(psmouse);
if (ret)
return ret;
return 0;
}
static int cypress_set_absolute_mode(struct psmouse *psmouse)
{
struct cytp_data *cytp = psmouse->private;
unsigned char param[3];
if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
return -1;
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 ret;
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);
ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
if (ret < 0)
return ret;
__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(INPUT_PROP_BUTTONPAD, input->propbit);
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_LEFT, input->keybit);
__set_bit(BTN_RIGHT, input->keybit);
__set_bit(BTN_MIDDLE, input->keybit);
input_set_drvdata(input, cytp);
return 0;
}
static int cypress_get_finger_count(unsigned char header_byte)
{
unsigned char 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 -1;
}
}
return finger_count;
}
static int cypress_parse_packet(struct psmouse *psmouse,
struct cytp_data *cytp, struct cytp_report_data *report_data)
{
unsigned char *packet = psmouse->packet;
unsigned char header_byte = packet[0];
int contact_cnt;
memset(report_data, 0, sizeof(struct cytp_report_data));
contact_cnt = cypress_get_finger_count(header_byte);
if (contact_cnt < 0) /* e.g. palm detect */
return -EINVAL;
report_data->contact_cnt = contact_cnt;
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;
if (cypress_parse_packet(psmouse, cytp, &report_data))
return;
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);
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;
unsigned char *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 (contact_cnt < 0)
return PSMOUSE_BAD_DATA;
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;
if (rate >= 80) {
psmouse->rate = 80;
cytp->mode |= CYTP_BIT_HIGH_RATE;
} else {
psmouse->rate = 40;
cytp->mode &= ~CYTP_BIT_HIGH_RATE;
}
ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
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 rc;
do {
cypress_reset(psmouse);
rc = cypress_detect(psmouse, false);
} while (rc && (--tries > 0));
if (rc) {
psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
return -1;
}
if (cypress_set_absolute_mode(psmouse)) {
psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
return -1;
}
return 0;
}
int cypress_init(struct psmouse *psmouse)
{
struct cytp_data *cytp;
cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
psmouse->private = (void *)cytp;
if (cytp == NULL)
return -ENOMEM;
cypress_reset(psmouse);
psmouse->pktsize = 8;
if (cypress_query_hardware(psmouse)) {
psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
goto err_exit;
}
if (cypress_set_absolute_mode(psmouse)) {
psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
goto err_exit;
}
if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
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 commmunicating with it as default PS2 mouse.
*/
cypress_reset(psmouse);
psmouse->private = NULL;
kfree(cytp);
return -1;
}
bool cypress_supported(void)
{
return true;
}

View File

@@ -0,0 +1,191 @@
#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_PS2_RETRY 0xfe
#define CYTP_PS2_ERROR 0xfc
#define CYTP_RESP_RETRY 0x01
#define CYTP_RESP_ERROR 0xfe
#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;
};
#ifdef CONFIG_MOUSE_PS2_CYPRESS
int cypress_detect(struct psmouse *psmouse, bool set_properties);
int cypress_init(struct psmouse *psmouse);
bool cypress_supported(void);
#else
inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
inline int cypress_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
inline bool cypress_supported(void)
{
return 0;
}
#endif /* CONFIG_MOUSE_PS2_CYPRESS */
#endif /* _CYPRESS_PS2_H */

View File

@@ -0,0 +1,90 @@
/*
* 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.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* 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
/* 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_FALLBACK_FW_NAME "elan_i2c.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_SIGNATURE_SIZE 6
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_baseliune, u8 *value);
int (*get_version)(struct i2c_client *client, bool iap, u8 *version);
int (*get_sm_version)(struct i2c_client *client,
u8* ic_type, u8 *version);
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
int (*get_product_id)(struct i2c_client *client, u8 *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);
int (*write_fw_block)(struct i2c_client *client,
const u8 *page, u16 checksum, int idx);
int (*finish_fw_update)(struct i2c_client *client,
struct completion *reset_done);
int (*get_report)(struct i2c_client *client, u8 *report);
int (*get_pressure_adjustment)(struct i2c_client *client,
int *adjustment);
};
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

View File

@@ -0,0 +1,643 @@
/*
* 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.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* 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/sched.h>
#include <asm/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_UNIQUEID_CMD 0x0101
#define ETP_I2C_FW_VERSION_CMD 0x0102
#define ETP_I2C_SM_VERSION_CMD 0x0103
#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_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_REPORT_LEN 34
#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_version(struct i2c_client *client,
bool iap, u8 *version)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client,
iap ? ETP_I2C_IAP_VERSION_CMD :
ETP_I2C_FW_VERSION_CMD,
val);
if (error) {
dev_err(&client->dev, "failed to get %s version: %d\n",
iap ? "IAP" : "FW", error);
return error;
}
*version = val[0];
return 0;
}
static int elan_i2c_get_sm_version(struct i2c_client *client,
u8 *ic_type, u8 *version)
{
int error;
u8 val[3];
error = elan_i2c_read_cmd(client, ETP_I2C_SM_VERSION_CMD, val);
if (error) {
dev_err(&client->dev, "failed to get SM version: %d\n", error);
return error;
}
*version = val[0];
*ic_type = val[1];
return 0;
}
static int elan_i2c_get_product_id(struct i2c_client *client, u8 *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 = val[0];
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) & 0x0fff;
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) & 0x0fff;
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_i2c_prepare_fw_update(struct i2c_client *client)
{
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;
}
/* 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,
const u8 *page, u16 checksum, int idx)
{
struct device *dev = &client->dev;
u8 page_store[ETP_FW_PAGE_SIZE + 4];
u8 val[3];
u16 result;
int ret, error;
page_store[0] = ETP_I2C_IAP_REG_L;
page_store[1] = ETP_I2C_IAP_REG_H;
memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
/* recode checksum at last two bytes */
put_unaligned_le16(checksum, &page_store[ETP_FW_PAGE_SIZE + 2]);
ret = i2c_master_send(client, page_store, sizeof(page_store));
if (ret != sizeof(page_store)) {
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(20);
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;
long ret;
int error;
int len;
u8 buffer[ETP_I2C_INF_LENGTH];
reinit_completion(completion);
enable_irq(client->irq);
error = elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, ETP_I2C_RESET);
if (!error)
ret = wait_for_completion_interruptible_timeout(completion,
msecs_to_jiffies(300));
disable_irq(client->irq);
if (error) {
dev_err(dev, "device reset failed: %d\n", error);
return error;
} else if (ret == 0) {
dev_err(dev, "timeout waiting for device reset\n");
return -ETIMEDOUT;
} else if (ret < 0) {
error = ret;
dev_err(dev, "error waiting for device reset: %d\n", 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(struct i2c_client *client, u8 *report)
{
int len;
len = i2c_master_recv(client, report, ETP_I2C_REPORT_LEN);
if (len < 0) {
dev_err(&client->dev, "failed to read report data: %d\n", len);
return len;
}
if (len != ETP_I2C_REPORT_LEN) {
dev_err(&client->dev,
"wrong report length (%d vs %d expected)\n",
len, ETP_I2C_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_report = elan_i2c_get_report,
};

View File

@@ -0,0 +1,523 @@
/*
* 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.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* 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_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[ETP_SMBUS_HELLOPACKET_LEN] = { 0, 0, 0, 0, 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;
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_CALIBRATE_QUERY, val);
if (error < 0)
return error;
return 0;
}
static int elan_smbus_get_baseline_data(struct i2c_client *client,
bool max_baseline, u8 *value)
{
int error;
u8 val[3];
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,
bool iap, u8 *version)
{
int error;
u8 val[3];
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 *ic_type, u8 *version)
{
int error;
u8 val[3];
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];
return 0;
}
static int elan_smbus_get_product_id(struct i2c_client *client, u8 *id)
{
int error;
u8 val[3];
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 = val[1];
return 0;
}
static int elan_smbus_get_checksum(struct i2c_client *client,
bool iap, u16 *csum)
{
int error;
u8 val[3];
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 error;
u8 val[3];
error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
if (error) {
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 error;
u8 val[3];
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_RESOLUTION_CMD, val);
if (error) {
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 error;
u8 val[3];
error = i2c_smbus_read_block_data(client,
ETP_SMBUS_XY_TRACENUM_CMD, val);
if (error) {
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[3];
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)
{
struct device *dev = &client->dev;
int len;
int error;
enum tp_mode mode;
u8 val[3];
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 < 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,
const u8 *page, u16 checksum, int idx)
{
struct device *dev = &client->dev;
int error;
u16 result;
u8 val[3];
/*
* 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,
ETP_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,
ETP_FW_PAGE_SIZE / 2,
page + ETP_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(struct i2c_client *client, u8 *report)
{
int 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 (len != ETP_SMBUS_REPORT_LEN) {
dev_err(&client->dev,
"wrong report length (%d vs %d expected)\n",
len, ETP_SMBUS_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;
}
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 = elan_smbus_get_report,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,156 @@
/*
* Elantech Touchpad driver (v6)
*
* Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* 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
/*
* 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
/*
* track up to 5 fingers for v4 hardware
*/
#define ETP_MAX_FINGERS 5
/*
* weight value for v4 hardware
*/
#define ETP_WEIGHT_VALUE 5
/*
* The base position for one finger, v4 hardware
*/
struct finger_pos {
unsigned int x;
unsigned int y;
};
struct elantech_data {
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 char debug;
unsigned char capabilities[3];
bool paritycheck;
bool jumpy_cursor;
bool reports_pressure;
unsigned char hw_version;
unsigned int fw_version;
unsigned int single_finger_reports;
unsigned int y_max;
unsigned int width;
struct finger_pos mt[ETP_MAX_FINGERS];
unsigned char parity[256];
int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
};
#ifdef CONFIG_MOUSE_PS2_ELANTECH
int elantech_detect(struct psmouse *psmouse, bool set_properties);
int elantech_init(struct psmouse *psmouse);
#else
static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
static inline int elantech_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_ELANTECH */
#endif

View File

@@ -0,0 +1,187 @@
/*
* Driver for simulating a mouse on GPIO lines.
*
* Copyright (C) 2007 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/gpio.h>
#include <linux/gpio_mouse.h>
/*
* Timer function which is run every scan_ms ms when the device is opened.
* The dev input variable is set to the the input_dev pointer.
*/
static void gpio_mouse_scan(struct input_polled_dev *dev)
{
struct gpio_mouse_platform_data *gpio = dev->private;
struct input_dev *input = dev->input;
int x, y;
if (gpio->bleft >= 0)
input_report_key(input, BTN_LEFT,
gpio_get_value(gpio->bleft) ^ gpio->polarity);
if (gpio->bmiddle >= 0)
input_report_key(input, BTN_MIDDLE,
gpio_get_value(gpio->bmiddle) ^ gpio->polarity);
if (gpio->bright >= 0)
input_report_key(input, BTN_RIGHT,
gpio_get_value(gpio->bright) ^ gpio->polarity);
x = (gpio_get_value(gpio->right) ^ gpio->polarity)
- (gpio_get_value(gpio->left) ^ gpio->polarity);
y = (gpio_get_value(gpio->down) ^ gpio->polarity)
- (gpio_get_value(gpio->up) ^ gpio->polarity);
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 gpio_mouse_platform_data *pdata = pdev->dev.platform_data;
struct input_polled_dev *input_poll;
struct input_dev *input;
int pin, i;
int error;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
error = -ENXIO;
goto out;
}
if (pdata->scan_ms < 0) {
dev_err(&pdev->dev, "invalid scan time\n");
error = -EINVAL;
goto out;
}
for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
pin = pdata->pins[i];
if (pin < 0) {
if (i <= GPIO_MOUSE_PIN_RIGHT) {
/* Mouse direction is required. */
dev_err(&pdev->dev,
"missing GPIO for directions\n");
error = -EINVAL;
goto out_free_gpios;
}
if (i == GPIO_MOUSE_PIN_BLEFT)
dev_dbg(&pdev->dev, "no left button defined\n");
} else {
error = gpio_request(pin, "gpio_mouse");
if (error) {
dev_err(&pdev->dev, "fail %d pin (%d idx)\n",
pin, i);
goto out_free_gpios;
}
gpio_direction_input(pin);
}
}
input_poll = input_allocate_polled_device();
if (!input_poll) {
dev_err(&pdev->dev, "not enough memory for input device\n");
error = -ENOMEM;
goto out_free_gpios;
}
platform_set_drvdata(pdev, input_poll);
/* set input-polldev handlers */
input_poll->private = pdata;
input_poll->poll = gpio_mouse_scan;
input_poll->poll_interval = pdata->scan_ms;
input = input_poll->input;
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_REL, REL_X);
input_set_capability(input, EV_REL, REL_Y);
if (pdata->bleft >= 0)
input_set_capability(input, EV_KEY, BTN_LEFT);
if (pdata->bmiddle >= 0)
input_set_capability(input, EV_KEY, BTN_MIDDLE);
if (pdata->bright >= 0)
input_set_capability(input, EV_KEY, BTN_RIGHT);
error = input_register_polled_device(input_poll);
if (error) {
dev_err(&pdev->dev, "could not register input device\n");
goto out_free_polldev;
}
dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n",
pdata->scan_ms,
pdata->bleft < 0 ? "" : "left ",
pdata->bmiddle < 0 ? "" : "middle ",
pdata->bright < 0 ? "" : "right");
return 0;
out_free_polldev:
input_free_polled_device(input_poll);
platform_set_drvdata(pdev, NULL);
out_free_gpios:
while (--i >= 0) {
pin = pdata->pins[i];
if (pin)
gpio_free(pin);
}
out:
return error;
}
static int gpio_mouse_remove(struct platform_device *pdev)
{
struct input_polled_dev *input = platform_get_drvdata(pdev);
struct gpio_mouse_platform_data *pdata = input->private;
int pin, i;
input_unregister_polled_device(input);
input_free_polled_device(input);
for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
pin = pdata->pins[i];
if (pin >= 0)
gpio_free(pin);
}
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver gpio_mouse_device_driver = {
.probe = gpio_mouse_probe,
.remove = gpio_mouse_remove,
.driver = {
.name = "gpio_mouse",
.owner = THIS_MODULE,
}
};
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 */

1067
drivers/input/mouse/hgpk.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
/*
* OLPC HGPK (XO-1) touchpad PS/2 mouse driver
*/
#ifndef _HGPK_H
#define _HGPK_H
#define HGPK_GS 0xff /* The GlideSensor */
#define HGPK_PT 0xcf /* The PenTablet */
enum hgpk_model_t {
HGPK_MODEL_PREA = 0x0a, /* pre-B1s */
HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */
HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */
HGPK_MODEL_C = 0x3c,
HGPK_MODEL_D = 0x50, /* C1, mass production */
};
enum hgpk_spew_flag {
NO_SPEW,
MAYBE_SPEWING,
SPEW_DETECTED,
RECALIBRATING,
};
#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */
enum hgpk_mode {
HGPK_MODE_MOUSE,
HGPK_MODE_GLIDESENSOR,
HGPK_MODE_PENTABLET,
HGPK_MODE_INVALID
};
struct hgpk_data {
struct psmouse *psmouse;
enum hgpk_mode mode;
bool powered;
enum hgpk_spew_flag spew_flag;
int spew_count, x_tally, y_tally; /* spew detection */
unsigned long recalib_window;
struct delayed_work recalib_wq;
int abs_x, abs_y;
int dupe_count;
int xbigj, ybigj, xlast, ylast; /* jumpiness detection */
int xsaw_secondary, ysaw_secondary; /* jumpiness detection */
};
#ifdef CONFIG_MOUSE_PS2_OLPC
void hgpk_module_init(void);
int hgpk_detect(struct psmouse *psmouse, bool set_properties);
int hgpk_init(struct psmouse *psmouse);
#else
static inline void hgpk_module_init(void)
{
}
static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENODEV;
}
static inline int hgpk_init(struct psmouse *psmouse)
{
return -ENODEV;
}
#endif
#endif

View File

@@ -0,0 +1,196 @@
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* Teemu Rantanen Derrick Cole
* Peter Cervasio Christoph Niemann
* Philip Blundell Russell King
* Bob Harris
*/
/*
* Inport (ATI XL and Microsoft) busmouse driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
MODULE_LICENSE("GPL");
#define INPORT_BASE 0x23c
#define INPORT_EXTENT 4
#define INPORT_CONTROL_PORT INPORT_BASE + 0
#define INPORT_DATA_PORT INPORT_BASE + 1
#define INPORT_SIGNATURE_PORT INPORT_BASE + 2
#define INPORT_REG_BTNS 0x00
#define INPORT_REG_X 0x01
#define INPORT_REG_Y 0x02
#define INPORT_REG_MODE 0x07
#define INPORT_RESET 0x80
#ifdef CONFIG_MOUSE_ATIXL
#define INPORT_NAME "ATI XL Mouse"
#define INPORT_VENDOR 0x0002
#define INPORT_SPEED_30HZ 0x01
#define INPORT_SPEED_50HZ 0x02
#define INPORT_SPEED_100HZ 0x03
#define INPORT_SPEED_200HZ 0x04
#define INPORT_MODE_BASE INPORT_SPEED_100HZ
#define INPORT_MODE_IRQ 0x08
#else
#define INPORT_NAME "Microsoft InPort Mouse"
#define INPORT_VENDOR 0x0001
#define INPORT_MODE_BASE 0x10
#define INPORT_MODE_IRQ 0x01
#endif
#define INPORT_MODE_HOLD 0x20
#define INPORT_IRQ 5
static int inport_irq = INPORT_IRQ;
module_param_named(irq, inport_irq, uint, 0);
MODULE_PARM_DESC(irq, "IRQ number (5=default)");
static struct input_dev *inport_dev;
static irqreturn_t inport_interrupt(int irq, void *dev_id)
{
unsigned char buttons;
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
outb(INPORT_REG_X, INPORT_CONTROL_PORT);
input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT));
outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT));
outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
buttons = inb(INPORT_DATA_PORT);
input_report_key(inport_dev, BTN_MIDDLE, buttons & 1);
input_report_key(inport_dev, BTN_LEFT, buttons & 2);
input_report_key(inport_dev, BTN_RIGHT, buttons & 4);
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
input_sync(inport_dev);
return IRQ_HANDLED;
}
static int inport_open(struct input_dev *dev)
{
if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
return -EBUSY;
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
return 0;
}
static void inport_close(struct input_dev *dev)
{
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
free_irq(inport_irq, NULL);
}
static int __init inport_init(void)
{
unsigned char a, b, c;
int err;
if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
return -EBUSY;
}
a = inb(INPORT_SIGNATURE_PORT);
b = inb(INPORT_SIGNATURE_PORT);
c = inb(INPORT_SIGNATURE_PORT);
if (a == b || a != c) {
printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
err = -ENODEV;
goto err_release_region;
}
inport_dev = input_allocate_device();
if (!inport_dev) {
printk(KERN_ERR "inport.c: Not enough memory for input device\n");
err = -ENOMEM;
goto err_release_region;
}
inport_dev->name = INPORT_NAME;
inport_dev->phys = "isa023c/input0";
inport_dev->id.bustype = BUS_ISA;
inport_dev->id.vendor = INPORT_VENDOR;
inport_dev->id.product = 0x0001;
inport_dev->id.version = 0x0100;
inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
inport_dev->open = inport_open;
inport_dev->close = inport_close;
outb(INPORT_RESET, INPORT_CONTROL_PORT);
outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
err = input_register_device(inport_dev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
input_free_device(inport_dev);
err_release_region:
release_region(INPORT_BASE, INPORT_EXTENT);
return err;
}
static void __exit inport_exit(void)
{
input_unregister_device(inport_dev);
release_region(INPORT_BASE, INPORT_EXTENT);
}
module_init(inport_init);
module_exit(inport_exit);

View File

@@ -0,0 +1,352 @@
/*
* 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.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/dmi.h>
#include <linux/slab.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 __initconst lifebook_dmi_table[] = {
{
/* 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;
unsigned char *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) {
input_report_rel(dev2, REL_X,
((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
input_report_rel(dev2, REL_Y,
-(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
}
input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
input_sync(dev2);
}
return PSMOUSE_FULL_PACKET;
}
static int lifebook_absolute_mode(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param;
if (psmouse_reset(psmouse))
return -1;
/*
* 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;
unsigned char param = 0x06;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
}
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
static const unsigned char params[] = { 0, 1, 2, 2, 3 };
unsigned char 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 -1;
if (desired_serio_phys &&
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
return -1;
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(sizeof(struct lifebook_data), GFP_KERNEL);
dev2 = input_allocate_device();
if (!priv || !dev2)
goto err_out;
priv->dev2 = dev2;
snprintf(priv->phys, sizeof(priv->phys),
"%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
dev2->name = "PS/2 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;
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
dev2->keybit[BIT_WORD(BTN_LEFT)] =
BIT_MASK(BTN_LEFT) | BIT_MASK(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;
if (lifebook_absolute_mode(psmouse))
return -1;
dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
dev1->relbit[0] = 0;
dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0;
dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(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) {
if (lifebook_create_relative_device(psmouse)) {
lifebook_relative_mode(psmouse);
return -1;
}
}
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;
}

View File

@@ -0,0 +1,32 @@
/*
* Fujitsu B-series Lifebook PS/2 TouchScreen driver
*
* Copyright (c) 2005 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _LIFEBOOK_H
#define _LIFEBOOK_H
#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
void lifebook_module_init(void);
int lifebook_detect(struct psmouse *psmouse, bool set_properties);
int lifebook_init(struct psmouse *psmouse);
#else
inline void lifebook_module_init(void)
{
}
inline int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
inline int lifebook_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif
#endif

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* Based on the work of:
* James Banks Matthew Dillon
* David Giller Nathan Laredo
* Linus Torvalds Johan Myreen
* Cliff Matthews Philip Blundell
* Russell King
*/
/*
* Logitech Bus Mouse Driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Logitech busmouse driver");
MODULE_LICENSE("GPL");
#define LOGIBM_BASE 0x23c
#define LOGIBM_EXTENT 4
#define LOGIBM_DATA_PORT LOGIBM_BASE + 0
#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1
#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2
#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3
#define LOGIBM_ENABLE_IRQ 0x00
#define LOGIBM_DISABLE_IRQ 0x10
#define LOGIBM_READ_X_LOW 0x80
#define LOGIBM_READ_X_HIGH 0xa0
#define LOGIBM_READ_Y_LOW 0xc0
#define LOGIBM_READ_Y_HIGH 0xe0
#define LOGIBM_DEFAULT_MODE 0x90
#define LOGIBM_CONFIG_BYTE 0x91
#define LOGIBM_SIGNATURE_BYTE 0xa5
#define LOGIBM_IRQ 5
static int logibm_irq = LOGIBM_IRQ;
module_param_named(irq, logibm_irq, uint, 0);
MODULE_PARM_DESC(irq, "IRQ number (5=default)");
static struct input_dev *logibm_dev;
static irqreturn_t logibm_interrupt(int irq, void *dev_id)
{
char dx, dy;
unsigned char buttons;
outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT);
dx = (inb(LOGIBM_DATA_PORT) & 0xf);
outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT);
dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4;
outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT);
dy = (inb(LOGIBM_DATA_PORT) & 0xf);
outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT);
buttons = inb(LOGIBM_DATA_PORT);
dy |= (buttons & 0xf) << 4;
buttons = ~buttons >> 5;
input_report_rel(logibm_dev, REL_X, dx);
input_report_rel(logibm_dev, REL_Y, dy);
input_report_key(logibm_dev, BTN_RIGHT, buttons & 1);
input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2);
input_report_key(logibm_dev, BTN_LEFT, buttons & 4);
input_sync(logibm_dev);
outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
return IRQ_HANDLED;
}
static int logibm_open(struct input_dev *dev)
{
if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) {
printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq);
return -EBUSY;
}
outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
return 0;
}
static void logibm_close(struct input_dev *dev)
{
outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
free_irq(logibm_irq, NULL);
}
static int __init logibm_init(void)
{
int err;
if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
return -EBUSY;
}
outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT);
outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT);
udelay(100);
if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
err = -ENODEV;
goto err_release_region;
}
outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
logibm_dev = input_allocate_device();
if (!logibm_dev) {
printk(KERN_ERR "logibm.c: Not enough memory for input device\n");
err = -ENOMEM;
goto err_release_region;
}
logibm_dev->name = "Logitech bus mouse";
logibm_dev->phys = "isa023c/input0";
logibm_dev->id.bustype = BUS_ISA;
logibm_dev->id.vendor = 0x0003;
logibm_dev->id.product = 0x0001;
logibm_dev->id.version = 0x0100;
logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
logibm_dev->open = logibm_open;
logibm_dev->close = logibm_close;
err = input_register_device(logibm_dev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
input_free_device(logibm_dev);
err_release_region:
release_region(LOGIBM_BASE, LOGIBM_EXTENT);
return err;
}
static void __exit logibm_exit(void)
{
input_unregister_device(logibm_dev);
release_region(LOGIBM_BASE, LOGIBM_EXTENT);
}
module_init(logibm_init);
module_exit(logibm_exit);

View File

@@ -0,0 +1,425 @@
/*
* Logitech PS/2++ mouse driver
*
* Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2003 Eric Wong <eric@yhbt.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.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 0x01
#define PS2PP_HWHEEL 0x02
#define PS2PP_SIDE_BTN 0x04
#define PS2PP_EXTRA_BTN 0x08
#define PS2PP_TASK_BTN 0x10
#define PS2PP_NAV_BTN 0x20
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;
unsigned char *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,
(int) (packet[2] & 8) - (int) (packet[2] & 7));
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
break;
case 0x0f: /* TouchPad extra info */
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
packet[0] = packet[2] | 0x08;
break;
default:
psmouse_dbg(psmouse,
"Received PS2++ packet #%x, but don't know how to handle.\n",
(packet[1] >> 4) | (packet[0] & 0x30));
break;
}
} else {
/* Standard PS/2 motion data */
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
}
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
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, unsigned char *param, unsigned char command)
{
if (psmouse_sliced_command(psmouse, command))
return -1;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300))
return -1;
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;
unsigned char 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;
unsigned char 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 reciver */
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)
__set_bit(BTN_SIDE, input_dev->keybit);
if (model_info->features & PS2PP_EXTRA_BTN)
__set_bit(BTN_EXTRA, input_dev->keybit);
if (model_info->features & PS2PP_TASK_BTN)
__set_bit(BTN_TASK, input_dev->keybit);
if (model_info->features & PS2PP_NAV_BTN) {
__set_bit(BTN_FORWARD, input_dev->keybit);
__set_bit(BTN_BACK, input_dev->keybit);
}
if (model_info->features & PS2PP_WHEEL)
__set_bit(REL_WHEEL, input_dev->relbit);
if (model_info->features & PS2PP_HWHEEL)
__set_bit(REL_HWHEEL, input_dev->relbit);
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;
}
}
/*
* 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_init(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char model, buttons;
const struct ps2pp_info *model_info;
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 -1;
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) {
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 -1;
}
}
}
if (buttons >= 3)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
}
return use_ps2pp ? 0 : -1;
}

View File

@@ -0,0 +1,23 @@
/*
* Logitech PS/2++ mouse driver header
*
* Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _LOGIPS2PP_H
#define _LOGIPS2PP_H
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
int ps2pp_init(struct psmouse *psmouse, bool set_properties);
#else
inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */
#endif

View File

@@ -0,0 +1,150 @@
/*
* 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 = maple_get_drvdata(to_maple_dev(&dev->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 = maple_get_drvdata(to_maple_dev(&dev->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(sizeof(struct dc_mouse), GFP_KERNEL);
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;
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;
maple_set_drvdata(mdev, mse);
return error;
fail_register:
input_free_device(input_dev);
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);

View File

@@ -0,0 +1,369 @@
/*
* Synaptics NavPoint (PXA27x SSP/SPI) driver.
*
* Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/input/navpoint.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/pxa2xx_ssp.h>
#include <linux/slab.h>
/*
* Synaptics Modular Embedded Protocol: Module Packet Format.
* Module header byte 2:0 = Length (# bytes that follow)
* Module header byte 4:3 = Control
* Module header byte 7:5 = Module Address
*/
#define HEADER_LENGTH(byte) ((byte) & 0x07)
#define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03)
#define HEADER_ADDRESS(byte) ((byte) >> 5)
struct navpoint {
struct ssp_device *ssp;
struct input_dev *input;
struct device *dev;
int gpio;
int index;
u8 data[1 + HEADER_LENGTH(0xff)];
};
/*
* Initialization values for SSCR0_x, SSCR1_x, SSSR_x.
*/
static const u32 sscr0 = 0
| SSCR0_TUM /* TIM = 1; No TUR interrupts */
| SSCR0_RIM /* RIM = 1; No ROR interrupts */
| SSCR0_SSE /* SSE = 1; SSP enabled */
| SSCR0_Motorola /* FRF = 0; Motorola SPI */
| SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */
;
static const u32 sscr1 = 0
| SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */
| SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */
| SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */
| SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */
| SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */
| SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */
| SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */
;
static const u32 sssr = 0
| SSSR_BCE /* BCE = 1; Clear BCE */
| SSSR_TUR /* TUR = 1; Clear TUR */
| SSSR_EOC /* EOC = 1; Clear EOC */
| SSSR_TINT /* TINT = 1; Clear TINT */
| SSSR_PINT /* PINT = 1; Clear PINT */
| SSSR_ROR /* ROR = 1; Clear ROR */
;
/*
* MEP Query $22: Touchpad Coordinate Range Query is not supported by
* the NavPoint module, so sampled values provide the default limits.
*/
#define NAVPOINT_X_MIN 1278
#define NAVPOINT_X_MAX 5340
#define NAVPOINT_Y_MIN 1572
#define NAVPOINT_Y_MAX 4396
#define NAVPOINT_PRESSURE_MIN 0
#define NAVPOINT_PRESSURE_MAX 255
static void navpoint_packet(struct navpoint *navpoint)
{
int finger;
int gesture;
int x, y, z;
switch (navpoint->data[0]) {
case 0xff: /* Garbage (packet?) between reset and Hello packet */
case 0x00: /* Module 0, NULL packet */
break;
case 0x0e: /* Module 0, Absolute packet */
finger = (navpoint->data[1] & 0x01);
gesture = (navpoint->data[1] & 0x02);
x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3];
y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5];
z = navpoint->data[6];
input_report_key(navpoint->input, BTN_TOUCH, finger);
input_report_abs(navpoint->input, ABS_X, x);
input_report_abs(navpoint->input, ABS_Y, y);
input_report_abs(navpoint->input, ABS_PRESSURE, z);
input_report_key(navpoint->input, BTN_TOOL_FINGER, finger);
input_report_key(navpoint->input, BTN_LEFT, gesture);
input_sync(navpoint->input);
break;
case 0x19: /* Module 0, Hello packet */
if ((navpoint->data[1] & 0xf0) == 0x10)
break;
/* FALLTHROUGH */
default:
dev_warn(navpoint->dev,
"spurious packet: data=0x%02x,0x%02x,...\n",
navpoint->data[0], navpoint->data[1]);
break;
}
}
static irqreturn_t navpoint_irq(int irq, void *dev_id)
{
struct navpoint *navpoint = dev_id;
struct ssp_device *ssp = navpoint->ssp;
irqreturn_t ret = IRQ_NONE;
u32 status;
status = pxa_ssp_read_reg(ssp, SSSR);
if (status & sssr) {
dev_warn(navpoint->dev,
"unexpected interrupt: status=0x%08x\n", status);
pxa_ssp_write_reg(ssp, SSSR, (status & sssr));
ret = IRQ_HANDLED;
}
while (status & SSSR_RNE) {
u32 data;
data = pxa_ssp_read_reg(ssp, SSDR);
navpoint->data[navpoint->index + 0] = (data >> 8);
navpoint->data[navpoint->index + 1] = data;
navpoint->index += 2;
if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) {
navpoint_packet(navpoint);
navpoint->index = 0;
}
status = pxa_ssp_read_reg(ssp, SSSR);
ret = IRQ_HANDLED;
}
return ret;
}
static void navpoint_up(struct navpoint *navpoint)
{
struct ssp_device *ssp = navpoint->ssp;
int timeout;
clk_prepare_enable(ssp->clk);
pxa_ssp_write_reg(ssp, SSCR1, sscr1);
pxa_ssp_write_reg(ssp, SSSR, sssr);
pxa_ssp_write_reg(ssp, SSTO, 0);
pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */
/* Wait until SSP port is ready for slave clock operations */
for (timeout = 100; timeout != 0; --timeout) {
if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS))
break;
msleep(1);
}
if (timeout == 0)
dev_err(navpoint->dev,
"timeout waiting for SSSR[CSS] to clear\n");
if (gpio_is_valid(navpoint->gpio))
gpio_set_value(navpoint->gpio, 1);
}
static void navpoint_down(struct navpoint *navpoint)
{
struct ssp_device *ssp = navpoint->ssp;
if (gpio_is_valid(navpoint->gpio))
gpio_set_value(navpoint->gpio, 0);
pxa_ssp_write_reg(ssp, SSCR0, 0);
clk_disable_unprepare(ssp->clk);
}
static int navpoint_open(struct input_dev *input)
{
struct navpoint *navpoint = input_get_drvdata(input);
navpoint_up(navpoint);
return 0;
}
static void navpoint_close(struct input_dev *input)
{
struct navpoint *navpoint = input_get_drvdata(input);
navpoint_down(navpoint);
}
static int navpoint_probe(struct platform_device *pdev)
{
const struct navpoint_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct ssp_device *ssp;
struct input_dev *input;
struct navpoint *navpoint;
int error;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
if (gpio_is_valid(pdata->gpio)) {
error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW,
"SYNAPTICS_ON");
if (error)
return error;
}
ssp = pxa_ssp_request(pdata->port, pdev->name);
if (!ssp) {
error = -ENODEV;
goto err_free_gpio;
}
/* HaRET does not disable devices before jumping into Linux */
if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
pxa_ssp_write_reg(ssp, SSCR0, 0);
dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port);
}
navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL);
input = input_allocate_device();
if (!navpoint || !input) {
error = -ENOMEM;
goto err_free_mem;
}
navpoint->ssp = ssp;
navpoint->input = input;
navpoint->dev = &pdev->dev;
navpoint->gpio = pdata->gpio;
input->name = pdev->name;
input->dev.parent = &pdev->dev;
__set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_LEFT, input->keybit);
__set_bit(BTN_TOUCH, input->keybit);
__set_bit(BTN_TOOL_FINGER, input->keybit);
input_set_abs_params(input, ABS_X,
NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0);
input_set_abs_params(input, ABS_Y,
NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0);
input_set_abs_params(input, ABS_PRESSURE,
NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX,
0, 0);
input->open = navpoint_open;
input->close = navpoint_close;
input_set_drvdata(input, navpoint);
error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint);
if (error)
goto err_free_mem;
error = input_register_device(input);
if (error)
goto err_free_irq;
platform_set_drvdata(pdev, navpoint);
dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq);
return 0;
err_free_irq:
free_irq(ssp->irq, &pdev->dev);
err_free_mem:
input_free_device(input);
kfree(navpoint);
pxa_ssp_free(ssp);
err_free_gpio:
if (gpio_is_valid(pdata->gpio))
gpio_free(pdata->gpio);
return error;
}
static int navpoint_remove(struct platform_device *pdev)
{
const struct navpoint_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct navpoint *navpoint = platform_get_drvdata(pdev);
struct ssp_device *ssp = navpoint->ssp;
free_irq(ssp->irq, navpoint);
input_unregister_device(navpoint->input);
kfree(navpoint);
pxa_ssp_free(ssp);
if (gpio_is_valid(pdata->gpio))
gpio_free(pdata->gpio);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int navpoint_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct navpoint *navpoint = platform_get_drvdata(pdev);
struct input_dev *input = navpoint->input;
mutex_lock(&input->mutex);
if (input->users)
navpoint_down(navpoint);
mutex_unlock(&input->mutex);
return 0;
}
static int navpoint_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct navpoint *navpoint = platform_get_drvdata(pdev);
struct input_dev *input = navpoint->input;
mutex_lock(&input->mutex);
if (input->users)
navpoint_up(navpoint);
mutex_unlock(&input->mutex);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume);
static struct platform_driver navpoint_driver = {
.probe = navpoint_probe,
.remove = navpoint_remove,
.driver = {
.name = "navpoint",
.owner = THIS_MODULE,
.pm = &navpoint_pm_ops,
},
};
module_platform_driver(navpoint_driver);
MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:navpoint");

View File

@@ -0,0 +1,179 @@
/*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Alan Cox Robin O'Leary
*/
/*
* IBM PC110 touchpad driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("IBM PC110 touchpad driver");
MODULE_LICENSE("GPL");
#define PC110PAD_OFF 0x30
#define PC110PAD_ON 0x38
static int pc110pad_irq = 10;
static int pc110pad_io = 0x15e0;
static struct input_dev *pc110pad_dev;
static int pc110pad_data[3];
static int pc110pad_count;
static irqreturn_t pc110pad_interrupt(int irq, void *ptr)
{
int value = inb_p(pc110pad_io);
int handshake = inb_p(pc110pad_io + 2);
outb(handshake | 1, pc110pad_io + 2);
udelay(2);
outb(handshake & ~1, pc110pad_io + 2);
udelay(2);
inb_p(0x64);
pc110pad_data[pc110pad_count++] = value;
if (pc110pad_count < 3)
return IRQ_HANDLED;
input_report_key(pc110pad_dev, BTN_TOUCH,
pc110pad_data[0] & 0x01);
input_report_abs(pc110pad_dev, ABS_X,
pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
input_report_abs(pc110pad_dev, ABS_Y,
pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
input_sync(pc110pad_dev);
pc110pad_count = 0;
return IRQ_HANDLED;
}
static void pc110pad_close(struct input_dev *dev)
{
outb(PC110PAD_OFF, pc110pad_io + 2);
}
static int pc110pad_open(struct input_dev *dev)
{
pc110pad_interrupt(0, NULL);
pc110pad_interrupt(0, NULL);
pc110pad_interrupt(0, NULL);
outb(PC110PAD_ON, pc110pad_io + 2);
pc110pad_count = 0;
return 0;
}
/*
* We try to avoid enabling the hardware if it's not
* there, but we don't know how to test. But we do know
* that the PC110 is not a PCI system. So if we find any
* PCI devices in the machine, we don't have a PC110.
*/
static int __init pc110pad_init(void)
{
int err;
if (!no_pci_devices())
return -ENODEV;
if (!request_region(pc110pad_io, 4, "pc110pad")) {
printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
pc110pad_io, pc110pad_io + 4);
return -EBUSY;
}
outb(PC110PAD_OFF, pc110pad_io + 2);
if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) {
printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
err = -EBUSY;
goto err_release_region;
}
pc110pad_dev = input_allocate_device();
if (!pc110pad_dev) {
printk(KERN_ERR "pc110pad: Not enough memory.\n");
err = -ENOMEM;
goto err_free_irq;
}
pc110pad_dev->name = "IBM PC110 TouchPad";
pc110pad_dev->phys = "isa15e0/input0";
pc110pad_dev->id.bustype = BUS_ISA;
pc110pad_dev->id.vendor = 0x0003;
pc110pad_dev->id.product = 0x0001;
pc110pad_dev->id.version = 0x0100;
pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff);
input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff);
pc110pad_dev->open = pc110pad_open;
pc110pad_dev->close = pc110pad_close;
err = input_register_device(pc110pad_dev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
input_free_device(pc110pad_dev);
err_free_irq:
free_irq(pc110pad_irq, NULL);
err_release_region:
release_region(pc110pad_io, 4);
return err;
}
static void __exit pc110pad_exit(void)
{
outb(PC110PAD_OFF, pc110pad_io + 2);
free_irq(pc110pad_irq, NULL);
input_unregister_device(pc110pad_dev);
release_region(pc110pad_io, 4);
}
module_init(pc110pad_init);
module_exit(pc110pad_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,184 @@
#ifndef _PSMOUSE_H
#define _PSMOUSE_H
#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;
struct psmouse {
void *private;
struct input_dev *dev;
struct ps2dev ps2dev;
struct delayed_work resync_work;
char *vendor;
char *name;
unsigned char packet[8];
unsigned char badbyte;
unsigned char pktcnt;
unsigned char pktsize;
unsigned char type;
bool ignore_parity;
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);
int (*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);
};
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,
PSMOUSE_ELANTECH,
PSMOUSE_FSP,
PSMOUSE_SYNAPTICS_RELATIVE,
PSMOUSE_CYPRESS,
PSMOUSE_AUTO /* This one should always be last */
};
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
unsigned long delay);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
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);
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__)
#endif /* _PSMOUSE_H */

View File

@@ -0,0 +1,257 @@
/*
* PXA930 track ball mouse driver
*
* Copyright (C) 2007 Marvell International Ltd.
* 2008-02-28: Yong Yao <yaoyong@marvell.com>
* initial version
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <mach/hardware.h>
#include <linux/platform_data/mouse-pxa930_trkball.h>
/* Trackball Controller Register Definitions */
#define TBCR (0x000C)
#define TBCNTR (0x0010)
#define TBSBC (0x0014)
#define TBCR_TBRST (1 << 1)
#define TBCR_TBSB (1 << 10)
#define TBCR_Y_FLT(n) (((n) & 0xf) << 6)
#define TBCR_X_FLT(n) (((n) & 0xf) << 2)
#define TBCNTR_YM(n) (((n) >> 24) & 0xff)
#define TBCNTR_YP(n) (((n) >> 16) & 0xff)
#define TBCNTR_XM(n) (((n) >> 8) & 0xff)
#define TBCNTR_XP(n) ((n) & 0xff)
#define TBSBC_TBSBC (0x1)
struct pxa930_trkball {
struct pxa930_trkball_platform_data *pdata;
/* Memory Mapped Register */
struct resource *mem;
void __iomem *mmio_base;
struct input_dev *input;
};
static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id)
{
struct pxa930_trkball *trkball = dev_id;
struct input_dev *input = trkball->input;
int tbcntr, x, y;
/* According to the spec software must read TBCNTR twice:
* if the read value is the same, the reading is valid
*/
tbcntr = __raw_readl(trkball->mmio_base + TBCNTR);
if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) {
x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2;
y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2;
input_report_rel(input, REL_X, x);
input_report_rel(input, REL_Y, y);
input_sync(input);
}
__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
__raw_writel(0, trkball->mmio_base + TBSBC);
return IRQ_HANDLED;
}
/* For TBCR, we need to wait for a while to make sure it has been modified. */
static int write_tbcr(struct pxa930_trkball *trkball, int v)
{
int i = 100;
__raw_writel(v, trkball->mmio_base + TBCR);
while (--i) {
if (__raw_readl(trkball->mmio_base + TBCR) == v)
break;
msleep(1);
}
if (i == 0) {
pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v);
return -ETIMEDOUT;
}
return 0;
}
static void pxa930_trkball_config(struct pxa930_trkball *trkball)
{
uint32_t tbcr;
/* According to spec, need to write the filters of x,y to 0xf first! */
tbcr = __raw_readl(trkball->mmio_base + TBCR);
write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf));
write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) |
TBCR_Y_FLT(trkball->pdata->y_filter));
/* According to spec, set TBCR_TBRST first, before clearing it! */
tbcr = __raw_readl(trkball->mmio_base + TBCR);
write_tbcr(trkball, tbcr | TBCR_TBRST);
write_tbcr(trkball, tbcr & ~TBCR_TBRST);
__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
__raw_writel(0, trkball->mmio_base + TBSBC);
pr_debug("%s: final TBCR=%x!\n", __func__,
__raw_readl(trkball->mmio_base + TBCR));
}
static int pxa930_trkball_open(struct input_dev *dev)
{
struct pxa930_trkball *trkball = input_get_drvdata(dev);
pxa930_trkball_config(trkball);
return 0;
}
static void pxa930_trkball_disable(struct pxa930_trkball *trkball)
{
uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR);
/* Held in reset, gate the 32-KHz input clock off */
write_tbcr(trkball, tbcr | TBCR_TBRST);
}
static void pxa930_trkball_close(struct input_dev *dev)
{
struct pxa930_trkball *trkball = input_get_drvdata(dev);
pxa930_trkball_disable(trkball);
}
static int pxa930_trkball_probe(struct platform_device *pdev)
{
struct pxa930_trkball *trkball;
struct input_dev *input;
struct resource *res;
int irq, error;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get trkball irq\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get register memory\n");
return -ENXIO;
}
trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL);
if (!trkball)
return -ENOMEM;
trkball->pdata = pdev->dev.platform_data;
if (!trkball->pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
error = -EINVAL;
goto failed;
}
trkball->mmio_base = ioremap_nocache(res->start, resource_size(res));
if (!trkball->mmio_base) {
dev_err(&pdev->dev, "failed to ioremap registers\n");
error = -ENXIO;
goto failed;
}
/* held the module in reset, will be enabled in open() */
pxa930_trkball_disable(trkball);
error = request_irq(irq, pxa930_trkball_interrupt, 0,
pdev->name, trkball);
if (error) {
dev_err(&pdev->dev, "failed to request irq: %d\n", error);
goto failed_free_io;
}
platform_set_drvdata(pdev, trkball);
input = input_allocate_device();
if (!input) {
dev_err(&pdev->dev, "failed to allocate input device\n");
error = -ENOMEM;
goto failed_free_irq;
}
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->open = pxa930_trkball_open;
input->close = pxa930_trkball_close;
input->dev.parent = &pdev->dev;
input_set_drvdata(input, trkball);
trkball->input = input;
input_set_capability(input, EV_REL, REL_X);
input_set_capability(input, EV_REL, REL_Y);
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "unable to register input device\n");
goto failed_free_input;
}
return 0;
failed_free_input:
input_free_device(input);
failed_free_irq:
free_irq(irq, trkball);
failed_free_io:
iounmap(trkball->mmio_base);
failed:
kfree(trkball);
return error;
}
static int pxa930_trkball_remove(struct platform_device *pdev)
{
struct pxa930_trkball *trkball = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
input_unregister_device(trkball->input);
free_irq(irq, trkball);
iounmap(trkball->mmio_base);
kfree(trkball);
return 0;
}
static struct platform_driver pxa930_trkball_driver = {
.driver = {
.name = "pxa930-trkball",
},
.probe = pxa930_trkball_probe,
.remove = pxa930_trkball_remove,
};
module_platform_driver(pxa930_trkball_driver);
MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>");
MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,116 @@
/*
* Acorn RiscPC mouse driver for Linux/ARM
*
* Copyright (c) 2000-2002 Vojtech Pavlik
* Copyright (C) 1996-2002 Russell King
*
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This 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

View File

@@ -0,0 +1,138 @@
/*-
* 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.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#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) */
};
#ifdef CONFIG_MOUSE_PS2_SENTELIC
extern int fsp_detect(struct psmouse *psmouse, bool set_properties);
extern int fsp_init(struct psmouse *psmouse);
#else
inline int fsp_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
inline int fsp_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif
#endif /* __KERNEL__ */
#endif /* !__SENTELIC_H */

View File

@@ -0,0 +1,358 @@
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Serial mouse driver for Linux
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.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;
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);
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(sizeof(struct sermouse), GFP_KERNEL);
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

View File

@@ -0,0 +1,189 @@
/*
* Synaptics TouchPad PS/2 mouse driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#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
/* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
#define SYN_BIT_HIGH_RATE (1 << 6)
#define SYN_BIT_SLEEP_MODE (1 << 3)
#define SYN_BIT_DISABLE_GESTURE (1 << 2)
#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1)
#define SYN_BIT_W_MODE (1 << 0)
/* synaptics model ID bits */
#define SYN_MODEL_ROT180(m) ((m) & (1 << 23))
#define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22))
#define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f)
#define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f)
#define SYN_MODEL_NEWABS(m) ((m) & (1 << 7))
#define SYN_MODEL_PEN(m) ((m) & (1 << 6))
#define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5))
#define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f)
/* synaptics capability bits */
#define SYN_CAP_EXTENDED(c) ((c) & (1 << 23))
#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18))
#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7))
#define SYN_CAP_SLEEP(c) ((c) & (1 << 4))
#define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3))
#define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1))
#define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0))
#define SYN_CAP_SUBMODEL_ID(c) (((c) & 0x00ff00) >> 8)
#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16)
/*
* 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 0x20 report min query 0x0f gives min coord reported
*/
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x002000)
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
#define SYN_MODE_RATE(m) ((m) & (1 << 6))
#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3))
#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2))
#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1))
#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
/* synaptics identify query bits */
#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
#define SYN_ID_MAJOR(i) ((i) & 0x0f)
#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
#define SYN_ID_FULL(i) ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i))
#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
#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
/* synaptics packet types */
#define SYN_NEWABS 0
#define SYN_NEWABS_STRICT 1
#define SYN_NEWABS_RELAXED 2
#define SYN_OLDABS 3
/* amount to fuzz position data when touchpad reports reduced filtering */
#define SYN_REDUCED_FILTER_FUZZ 8
/*
* A structure to describe which internal touchpad finger slots are being
* reported in raw packets.
*/
struct synaptics_mt_state {
int count; /* num fingers being tracked */
int sgm; /* which slot is reported by sgm pkt */
int agm; /* which slot is reported by agm pkt*/
};
/*
* 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;
unsigned char ext_buttons;
signed char scroll;
/* As reported in last AGM-CONTACT packets */
struct synaptics_mt_state mt_state;
};
struct synaptics_data {
/* Data read from the touchpad */
unsigned long int model_id; /* Model-ID */
unsigned long int firmware_id; /* Firmware-ID */
unsigned long int board_id; /* Board-ID */
unsigned long int capabilities; /* Capabilities */
unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
unsigned long int identity; /* Identification */
unsigned int x_res, y_res; /* X/Y resolution in units/mm */
unsigned int x_max, y_max; /* Max coordinates (from FW) */
unsigned int x_min, y_min; /* Min coordinates (from FW) */
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char 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 */
struct synaptics_mt_state mt_state; /* Current mt finger state */
bool mt_state_lost; /* mt_state may be incorrect */
/*
* 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;
bool agm_pending; /* new AGM packet received */
};
void synaptics_module_init(void);
int synaptics_detect(struct psmouse *psmouse, bool set_properties);
int synaptics_init(struct psmouse *psmouse);
int synaptics_init_relative(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
bool synaptics_supported(void);
#endif /* _SYNAPTICS_H */

View File

@@ -0,0 +1,675 @@
/*
* 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_MS 3
/* 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 = 1;
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;
spinlock_t lock;
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 ret;
ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (ret == 0)
ret = i2c_smbus_read_byte_data(client, reg & 0xff);
return ret;
}
static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (ret == 0)
ret = i2c_smbus_write_byte_data(client, reg & 0xff, val);
return ret;
}
static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (ret == 0)
ret = i2c_smbus_read_word_data(client, reg & 0xff);
return ret;
}
static int synaptics_i2c_config(struct i2c_client *client)
{
int ret, control;
u8 int_en;
/* set Report Rate to Device Highest (>=80) and Sleep to normal */
ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1);
if (ret)
return ret;
/* set Interrupt Disable to Func20 / Enable to Func10) */
int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK;
ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en);
if (ret)
return ret;
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;
ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control);
if (ret)
return ret;
return 0;
}
static int synaptics_i2c_reset_config(struct i2c_client *client)
{
int ret;
/* Reset the Touchpad */
ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND);
if (ret) {
dev_err(&client->dev, "Unable to reset device\n");
} else {
msleep(SOFT_RESET_DELAY_MS);
ret = synaptics_i2c_config(client);
if (ret)
dev_err(&client->dev, "Unable to config device\n");
}
return ret;
}
static int synaptics_i2c_check_error(struct i2c_client *client)
{
int status, ret = 0;
status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) &
(CONFIGURED_MSK | ERROR_MSK);
if (status != CONFIGURED_MSK)
ret = synaptics_i2c_reset_config(client);
return ret;
}
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 spontanious resets and errors */
if (synaptics_i2c_check_error(touch->client))
return 0;
/* 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 void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
unsigned long delay)
{
unsigned long flags;
spin_lock_irqsave(&touch->lock, flags);
mod_delayed_work(system_wq, &touch->dwork, delay);
spin_unlock_irqrestore(&touch->lock, flags);
}
static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
{
struct synaptics_i2c *touch = dev_id;
synaptics_i2c_reschedule_work(touch, 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);
} else {
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.
*/
synaptics_i2c_reschedule_work(touch, delay);
}
static int synaptics_i2c_open(struct input_dev *input)
{
struct synaptics_i2c *touch = input_get_drvdata(input);
int ret;
ret = synaptics_i2c_reset_config(touch->client);
if (ret)
return ret;
if (polling_req)
synaptics_i2c_reschedule_work(touch,
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->dev.parent = &touch->client->dev;
input->open = synaptics_i2c_open;
input->close = synaptics_i2c_close;
input_set_drvdata(input, touch);
/* Register the device as mouse */
__set_bit(EV_REL, input->evbit);
__set_bit(REL_X, input->relbit);
__set_bit(REL_Y, input->relbit);
/* Register device's buttons and keys */
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_LEFT, input->keybit);
}
static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
{
struct synaptics_i2c *touch;
touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL);
if (!touch)
return NULL;
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);
spin_lock_init(&touch->lock);
return touch;
}
static int synaptics_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int ret;
struct synaptics_i2c *touch;
touch = synaptics_i2c_touch_create(client);
if (!touch)
return -ENOMEM;
ret = synaptics_i2c_reset_config(client);
if (ret)
goto err_mem_free;
if (client->irq < 1)
polling_req = true;
touch->input = input_allocate_device();
if (!touch->input) {
ret = -ENOMEM;
goto err_mem_free;
}
synaptics_i2c_set_input_params(touch);
if (!polling_req) {
dev_dbg(&touch->client->dev,
"Requesting IRQ: %d\n", touch->client->irq);
ret = request_irq(touch->client->irq, synaptics_i2c_irq,
IRQ_TYPE_EDGE_FALLING,
DRIVER_NAME, touch);
if (ret) {
dev_warn(&touch->client->dev,
"IRQ request failed: %d, "
"falling back to polling\n", ret);
polling_req = true;
synaptics_i2c_reg_set(touch->client,
INTERRUPT_EN_REG, 0);
}
}
if (polling_req)
dev_dbg(&touch->client->dev,
"Using polling at rate: %d times/sec\n", scan_rate);
/* Register the device in input subsystem */
ret = input_register_device(touch->input);
if (ret) {
dev_err(&client->dev,
"Input device register failed: %d\n", ret);
goto err_input_free;
}
i2c_set_clientdata(client, touch);
return 0;
err_input_free:
input_free_device(touch->input);
err_mem_free:
kfree(touch);
return ret;
}
static int synaptics_i2c_remove(struct i2c_client *client)
{
struct synaptics_i2c *touch = i2c_get_clientdata(client);
if (!polling_req)
free_irq(client->irq, touch);
input_unregister_device(touch->input);
kfree(touch);
return 0;
}
#ifdef CONFIG_PM_SLEEP
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)
{
int ret;
struct i2c_client *client = to_i2c_client(dev);
struct synaptics_i2c *touch = i2c_get_clientdata(client);
ret = synaptics_i2c_reset_config(client);
if (ret)
return ret;
synaptics_i2c_reschedule_work(touch,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend,
synaptics_i2c_resume);
static const struct i2c_device_id synaptics_i2c_id_table[] = {
{ "synaptics_i2c", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
static struct i2c_driver synaptics_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &synaptics_i2c_pm,
},
.probe = synaptics_i2c_probe,
.remove = synaptics_i2c_remove,
.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");

View File

@@ -0,0 +1,557 @@
/*
* 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
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* 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/init.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;
/* 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 struct usb_endpoint_descriptor *
synusb_get_in_endpoint(struct usb_host_interface *iface)
{
struct usb_endpoint_descriptor *endpoint;
int i;
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
endpoint = &iface->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint)) {
/* we found our interrupt in endpoint */
return endpoint;
}
}
return NULL;
}
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;
}
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;
out:
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);
usb_kill_urb(synusb->urb);
synusb->intf->needs_remote_wakeup = 0;
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;
}
ep = synusb_get_in_endpoint(intf->cur_altsetting);
if (!ep)
return -ENODEV;
synusb = kzalloc(sizeof(*synusb), GFP_KERNEL);
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;
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)
strlcpy(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);
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);
}
__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);
struct input_dev *input_dev = synusb->input;
mutex_lock(&input_dev->mutex);
usb_kill_urb(synusb->urb);
mutex_unlock(&input_dev->mutex);
return 0;
}
static int synusb_resume(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
int retval = 0;
mutex_lock(&input_dev->mutex);
if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO;
}
mutex_unlock(&input_dev->mutex);
return retval;
}
static int synusb_pre_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
mutex_lock(&input_dev->mutex);
usb_kill_urb(synusb->urb);
return 0;
}
static int synusb_post_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
struct input_dev *input_dev = synusb->input;
int retval = 0;
if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO;
}
mutex_unlock(&input_dev->mutex);
return retval;
}
static int synusb_reset_resume(struct usb_interface *intf)
{
return synusb_resume(intf);
}
static 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");

View File

@@ -0,0 +1,100 @@
/* ----------------------------------------------------------------------------
* 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)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 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;
}

View File

@@ -0,0 +1,25 @@
/* ----------------------------------------------------------------------------
* touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens
*
* Copyright (C) 2005 by Stefan Lucke
* Copyright (c) 2005 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _TOUCHKIT_PS2_H
#define _TOUCHKIT_PS2_H
#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties);
#else
static inline int touchkit_ps2_detect(struct psmouse *psmouse,
bool set_properties)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_TOUCHKIT */
#endif

View File

@@ -0,0 +1,427 @@
/*
* Stephen Evanchik <evanchsa@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Trademarks are the property of their respective owners.
*/
#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 <asm/uaccess.h>
#include "psmouse.h"
#include "trackpoint.h"
/*
* 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)
{
unsigned char results[2];
int tries = 0;
/* Issue POR command, and repeat up to once if 0xFC00 received */
do {
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
return -1;
} while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
/* Check for success response -- 0xAA00 */
if (results[0] != 0xAA || results[1] != 0x00)
return -1;
return 0;
}
/*
* Device IO: read, write and toggle bit
*/
static int trackpoint_read(struct ps2dev *ps2dev,
unsigned char loc, unsigned char *results)
{
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
return -1;
}
return 0;
}
static int trackpoint_write(struct ps2dev *ps2dev,
unsigned char loc, unsigned char val)
{
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
return -1;
}
return 0;
}
static int trackpoint_toggle_bit(struct ps2dev *ps2dev,
unsigned char loc, unsigned char mask)
{
/* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F)
return -1;
if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
return -1;
}
return 0;
}
static int trackpoint_update_bit(struct ps2dev *ps2dev, unsigned char loc,
unsigned char mask, unsigned char value)
{
int retval = 0;
unsigned char data;
trackpoint_read(ps2dev, loc, &data);
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;
unsigned char command;
unsigned char mask;
unsigned char inverted;
unsigned char 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;
unsigned char value = *(unsigned char *)((char *)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;
unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
unsigned char value;
int err;
err = kstrtou8(buf, 10, &value);
if (err)
return err;
*field = value;
trackpoint_write(&psmouse->ps2dev, attr->command, value);
return 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;
unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
unsigned int value;
int err;
err = kstrtouint(buf, 10, &value);
if (err)
return err;
if (value > 1)
return -EINVAL;
if (attr->inverted)
value = !value;
if (*field != value) {
*field = value;
trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);
}
return 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)
#define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name) \
do { \
struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \
\
trackpoint_update_bit(&_psmouse->ps2dev, \
_attr->command, _attr->mask, _tp->_name); \
} while (0)
#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \
do { \
if (!_power_on || \
_tp->_name != trackpoint_attr_##_name.power_on_default) { \
if (!trackpoint_attr_##_name.mask) \
trackpoint_write(&_psmouse->ps2dev, \
trackpoint_attr_##_name.command, \
_tp->_name); \
else \
TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name); \
} \
} while (0)
#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \
(_tp->_name = trackpoint_attr_##_name.power_on_default)
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_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0,
TP_DEF_PTSON);
TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0,
TP_DEF_SKIPBACK);
TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1,
TP_DEF_EXT_DEV);
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_press_to_select.dattr.attr,
&psmouse_attr_skipback.dattr.attr,
&psmouse_attr_ext_dev.dattr.attr,
NULL
};
static struct attribute_group trackpoint_attr_group = {
.attrs = trackpoint_attrs,
};
static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id)
{
unsigned char param[2] = { 0 };
if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
return -1;
if (param[0] != TP_MAGIC_IDENT)
return -1;
if (firmware_id)
*firmware_id = param[1];
return 0;
}
/*
* 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) {
/*
* 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);
/* 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, 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)
{
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group);
kfree(psmouse->private);
psmouse->private = NULL;
}
static int trackpoint_reconnect(struct psmouse *psmouse)
{
int reset_fail;
int ret;
int tries = 2;
do {
ret = trackpoint_start_protocol(psmouse, NULL);
} while (ret != 0 && --tries > 0);
if (ret != 0)
return -1;
reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev);
if (trackpoint_sync(psmouse, !reset_fail))
return -1;
return 0;
}
int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char firmware_id;
unsigned char button_info;
int error;
if (trackpoint_start_protocol(psmouse, &firmware_id))
return -1;
if (!set_properties)
return 0;
if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
psmouse_warn(psmouse, "failed to get extended button data\n");
button_info = 0;
}
psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
if (!psmouse->private)
return -ENOMEM;
psmouse->vendor = "IBM";
psmouse->name = "TrackPoint";
psmouse->reconnect = trackpoint_reconnect;
psmouse->disconnect = trackpoint_disconnect;
if ((button_info & 0x0f) >= 3)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
trackpoint_defaults(psmouse->private);
error = trackpoint_power_on_reset(&psmouse->ps2dev);
/* Write defaults to TP only if reset fails. */
if (error)
trackpoint_sync(psmouse, false);
error = sysfs_create_group(&ps2dev->serio->dev.kobj, &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,
"IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
firmware_id,
(button_info & 0xf0) >> 4, button_info & 0x0f);
return 0;
}

View File

@@ -0,0 +1,156 @@
/*
* IBM TrackPoint PS/2 mouse driver
*
* Stephen Evanchik <evanchsa@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#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 */
#define TP_MAGIC_IDENT 0x01 /* Sent after a TP_READ_ID followed */
/* by the firmware ID */
/*
* 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_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 */
/*
* 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
/* 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
/* 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
{
unsigned char sensitivity, speed, inertia, reach;
unsigned char draghys, mindrag;
unsigned char thresh, upthresh;
unsigned char ztime, jenks;
/* toggles */
unsigned char press_to_select;
unsigned char skipback;
unsigned char ext_dev;
};
#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
int trackpoint_detect(struct psmouse *psmouse, bool set_properties);
#else
inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
#endif /* CONFIG_MOUSE_PS2_TRACKPOINT */
#endif /* _TRACKPOINT_H */

View File

@@ -0,0 +1,551 @@
/*
* 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).
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* 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>
#include <linux/init.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 - 1, 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:
strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
sizeof(mouse->name));
break;
case 0x04:
strlcpy(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(sizeof(struct vsxxxaa), GFP_KERNEL);
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);