Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
128
drivers/input/mouse/Kconfig
Normal file
128
drivers/input/mouse/Kconfig
Normal file
@@ -0,0 +1,128 @@
|
||||
#
|
||||
# Mouse driver configuration
|
||||
#
|
||||
menuconfig INPUT_MOUSE
|
||||
bool "Mouse"
|
||||
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_PC
|
||||
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 TouchPad users might be interested in a specialized
|
||||
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>
|
||||
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_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_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_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_HIL
|
||||
tristate "HIL pointers (mice etc)."
|
||||
depends on GSC || HP300
|
||||
select HP_SDC
|
||||
select HIL_MLC
|
||||
help
|
||||
Say Y here to support HIL pointers.
|
||||
|
||||
endif
|
||||
17
drivers/input/mouse/Makefile
Normal file
17
drivers/input/mouse/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Makefile for the mouse drivers.
|
||||
#
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
||||
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
|
||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
||||
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
|
||||
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
|
||||
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
|
||||
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
||||
|
||||
psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o trackpoint.o
|
||||
518
drivers/input/mouse/alps.c
Normal file
518
drivers/input/mouse/alps.c
Normal file
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
* ALPS touchpad PS/2 mouse driver
|
||||
*
|
||||
* Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
|
||||
* Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
|
||||
* Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
|
||||
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
*
|
||||
* ALPS detection, tap switching and status querying info is taken from
|
||||
* tpconfig utility (by C. Scott Ananian and Bruce Kall).
|
||||
*
|
||||
* 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 "alps.h"
|
||||
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
|
||||
#else
|
||||
#define dbg(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define ALPS_DUALPOINT 0x01
|
||||
#define ALPS_WHEEL 0x02
|
||||
#define ALPS_FW_BK_1 0x04
|
||||
#define ALPS_4BTN 0x08
|
||||
#define ALPS_OLDPROTO 0x10
|
||||
#define ALPS_PASS 0x20
|
||||
#define ALPS_FW_BK_2 0x40
|
||||
|
||||
static const struct alps_model_info alps_model_data[] = {
|
||||
{ { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */
|
||||
{ { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
|
||||
{ { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
|
||||
{ { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 }, /* HP ze1115 */
|
||||
{ { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
|
||||
{ { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
|
||||
{ { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */
|
||||
{ { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */
|
||||
{ { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
|
||||
{ { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
|
||||
{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */
|
||||
{ { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
|
||||
{ { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
|
||||
{ { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
|
||||
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
|
||||
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX - this entry is suspicious. First byte has zero lower nibble,
|
||||
* which is what a normal mouse would report. Also, the value 0x0e
|
||||
* isn't valid per PS/2 spec.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ALPS abolute Mode - new format
|
||||
*
|
||||
* byte 0: 1 ? ? ? 1 ? ? ?
|
||||
* byte 1: 0 x6 x5 x4 x3 x2 x1 x0
|
||||
* byte 2: 0 x10 x9 x8 x7 ? fin ges
|
||||
* byte 3: 0 y9 y8 y7 1 M R L
|
||||
* byte 4: 0 y6 y5 y4 y3 y2 y1 y0
|
||||
* byte 5: 0 z6 z5 z4 z3 z2 z1 z0
|
||||
*
|
||||
* ?'s can have different meanings on different models,
|
||||
* such as wheel rotation, extra buttons, stick buttons
|
||||
* on a dualpoint, etc.
|
||||
*/
|
||||
|
||||
static void alps_process_packet(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct input_dev *dev2 = priv->dev2;
|
||||
int x, y, z, ges, fin, left, right, middle;
|
||||
int back = 0, forward = 0;
|
||||
|
||||
if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */
|
||||
input_report_key(dev2, BTN_LEFT, packet[0] & 1);
|
||||
input_report_key(dev2, BTN_RIGHT, packet[0] & 2);
|
||||
input_report_key(dev2, BTN_MIDDLE, packet[0] & 4);
|
||||
input_report_rel(dev2, REL_X,
|
||||
packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
|
||||
input_report_rel(dev2, REL_Y,
|
||||
packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
|
||||
input_sync(dev2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->i->flags & ALPS_OLDPROTO) {
|
||||
left = packet[2] & 0x10;
|
||||
right = packet[2] & 0x08;
|
||||
middle = 0;
|
||||
x = packet[1] | ((packet[0] & 0x07) << 7);
|
||||
y = packet[4] | ((packet[3] & 0x07) << 7);
|
||||
z = packet[5];
|
||||
} else {
|
||||
left = packet[3] & 1;
|
||||
right = packet[3] & 2;
|
||||
middle = packet[3] & 4;
|
||||
x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
|
||||
y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
|
||||
z = packet[5];
|
||||
}
|
||||
|
||||
if (priv->i->flags & ALPS_FW_BK_1) {
|
||||
back = packet[2] & 4;
|
||||
forward = packet[0] & 0x10;
|
||||
}
|
||||
|
||||
if (priv->i->flags & ALPS_FW_BK_2) {
|
||||
back = packet[3] & 4;
|
||||
forward = packet[2] & 4;
|
||||
if ((middle = forward && back))
|
||||
forward = back = 0;
|
||||
}
|
||||
|
||||
ges = packet[2] & 1;
|
||||
fin = packet[2] & 2;
|
||||
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
input_report_key(dev, BTN_MIDDLE, middle);
|
||||
|
||||
if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) {
|
||||
input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
|
||||
input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
|
||||
input_sync(dev);
|
||||
input_sync(dev2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert hardware tap to a reasonable Z value */
|
||||
if (ges && !fin) z = 40;
|
||||
|
||||
/*
|
||||
* A "tap and drag" operation is reported by the hardware as a transition
|
||||
* from (!fin && ges) to (fin && ges). This should be translated to the
|
||||
* sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually.
|
||||
*/
|
||||
if (ges && fin && !priv->prev_fin) {
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
input_report_abs(dev, ABS_PRESSURE, 0);
|
||||
input_report_key(dev, BTN_TOOL_FINGER, 0);
|
||||
input_sync(dev);
|
||||
}
|
||||
priv->prev_fin = fin;
|
||||
|
||||
if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
|
||||
if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
|
||||
|
||||
if (z > 0) {
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_PRESSURE, z);
|
||||
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
|
||||
|
||||
if (priv->i->flags & ALPS_WHEEL)
|
||||
input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
|
||||
|
||||
if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
|
||||
input_report_key(dev, BTN_FORWARD, forward);
|
||||
input_report_key(dev, BTN_BACK, back);
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
|
||||
if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
|
||||
if (psmouse->pktcnt == 3) {
|
||||
alps_process_packet(psmouse);
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
}
|
||||
|
||||
if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0)
|
||||
return PSMOUSE_BAD_DATA;
|
||||
|
||||
/* Bytes 2 - 6 should have 0 in the highest bit */
|
||||
if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
|
||||
(psmouse->packet[psmouse->pktcnt - 1] & 0x80))
|
||||
return PSMOUSE_BAD_DATA;
|
||||
|
||||
if (psmouse->pktcnt == 6) {
|
||||
alps_process_packet(psmouse);
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
}
|
||||
|
||||
static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
|
||||
unsigned char param[4];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* First try "E6 report".
|
||||
* ALPS should return 0,0,10 or 0,0,100
|
||||
*/
|
||||
param[0] = 0;
|
||||
if (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))
|
||||
return NULL;
|
||||
|
||||
param[0] = param[1] = param[2] = 0xff;
|
||||
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
|
||||
return NULL;
|
||||
|
||||
dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
|
||||
|
||||
if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Now try "E7 report". Allowed responses are in
|
||||
* alps_model_data[].signature
|
||||
*/
|
||||
param[0] = 0;
|
||||
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21))
|
||||
return NULL;
|
||||
|
||||
param[0] = param[1] = param[2] = 0xff;
|
||||
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
|
||||
return NULL;
|
||||
|
||||
dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++);
|
||||
*version = (param[0] << 8) | (param[1] << 4) | i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(alps_model_data); i++)
|
||||
if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature)))
|
||||
return alps_model_data + i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For DualPoint devices select the device that should respond to
|
||||
* subsequent commands. It looks like glidepad is behind stickpointer,
|
||||
* I'd thought it would be other way around...
|
||||
*/
|
||||
static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
|
||||
|
||||
if (ps2_command(ps2dev, NULL, cmd) ||
|
||||
ps2_command(ps2dev, NULL, cmd) ||
|
||||
ps2_command(ps2dev, NULL, cmd) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
|
||||
return -1;
|
||||
|
||||
/* we may get 3 more bytes, just ignore them */
|
||||
ps2_drain(ps2dev, 3, 100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_absolute_mode(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
|
||||
/* Try ALPS magic knock - 4 disable before enable */
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Switch mouse to poll (remote) mode so motion data will not
|
||||
* get in our way
|
||||
*/
|
||||
return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
|
||||
}
|
||||
|
||||
static int alps_get_status(struct psmouse *psmouse, char *param)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
|
||||
/* Get status: 0xF5 0xF5 0xF5 0xE9 */
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
|
||||
return -1;
|
||||
|
||||
dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn touchpad tapping on or off. The sequences are:
|
||||
* 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
|
||||
* 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
|
||||
* My guess that 0xE9 (GetInfo) is here as a sync point.
|
||||
* For models that also have stickpointer (DualPoints) its tapping
|
||||
* is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
|
||||
* we don't fiddle with it.
|
||||
*/
|
||||
static int alps_tap_mode(struct psmouse *psmouse, int enable)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
|
||||
unsigned char tap_arg = enable ? 0x0A : 0x00;
|
||||
unsigned char param[4];
|
||||
|
||||
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
|
||||
ps2_command(ps2dev, &tap_arg, cmd))
|
||||
return -1;
|
||||
|
||||
if (alps_get_status(psmouse, param))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* alps_poll() - poll the touchpad for current motion packet.
|
||||
* Used in resync.
|
||||
*/
|
||||
static int alps_poll(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char buf[6];
|
||||
int poll_failed;
|
||||
|
||||
if (priv->i->flags & ALPS_PASS)
|
||||
alps_passthrough_mode(psmouse, 1);
|
||||
|
||||
poll_failed = ps2_command(&psmouse->ps2dev, buf,
|
||||
PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
|
||||
|
||||
if (priv->i->flags & ALPS_PASS)
|
||||
alps_passthrough_mode(psmouse, 0);
|
||||
|
||||
if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0)
|
||||
return -1;
|
||||
|
||||
if ((psmouse->badbyte & 0xc8) == 0x08) {
|
||||
/*
|
||||
* Poll the track stick ...
|
||||
*/
|
||||
if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8)))
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(psmouse->packet, buf, sizeof(buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_reconnect(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
int version;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
if (!(priv->i = alps_get_model(psmouse, &version)))
|
||||
return -1;
|
||||
|
||||
if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1))
|
||||
return -1;
|
||||
|
||||
if (alps_tap_mode(psmouse, 1)) {
|
||||
printk(KERN_WARNING "alps.c: Failed to reenable hardware tapping\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (alps_absolute_mode(psmouse)) {
|
||||
printk(KERN_ERR "alps.c: Failed to reenable absolute mode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_disconnect(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
input_unregister_device(priv->dev2);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
int alps_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv;
|
||||
struct input_dev *dev1 = psmouse->dev, *dev2;
|
||||
int version;
|
||||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
|
||||
dev2 = input_allocate_device();
|
||||
if (!priv || !dev2)
|
||||
goto init_fail;
|
||||
|
||||
priv->dev2 = dev2;
|
||||
|
||||
if (!(priv->i = alps_get_model(psmouse, &version)))
|
||||
goto init_fail;
|
||||
|
||||
if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1))
|
||||
goto init_fail;
|
||||
|
||||
if (alps_tap_mode(psmouse, 1))
|
||||
printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
|
||||
|
||||
if (alps_absolute_mode(psmouse)) {
|
||||
printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0))
|
||||
goto init_fail;
|
||||
|
||||
dev1->evbit[LONG(EV_KEY)] |= BIT(EV_KEY);
|
||||
dev1->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
|
||||
dev1->keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER);
|
||||
dev1->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
|
||||
dev1->evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
|
||||
input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
|
||||
if (priv->i->flags & ALPS_WHEEL) {
|
||||
dev1->evbit[LONG(EV_REL)] |= BIT(EV_REL);
|
||||
dev1->relbit[LONG(REL_WHEEL)] |= BIT(REL_WHEEL);
|
||||
}
|
||||
|
||||
if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
|
||||
dev1->keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD);
|
||||
dev1->keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK);
|
||||
}
|
||||
|
||||
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
|
||||
dev2->phys = priv->phys;
|
||||
dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
|
||||
dev2->id.bustype = BUS_I8042;
|
||||
dev2->id.vendor = 0x0002;
|
||||
dev2->id.product = PSMOUSE_ALPS;
|
||||
dev2->id.version = 0x0000;
|
||||
|
||||
dev2->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
dev2->relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y);
|
||||
dev2->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
|
||||
input_register_device(priv->dev2);
|
||||
|
||||
psmouse->protocol_handler = alps_process_byte;
|
||||
psmouse->poll = alps_poll;
|
||||
psmouse->disconnect = alps_disconnect;
|
||||
psmouse->reconnect = alps_reconnect;
|
||||
psmouse->pktsize = 6;
|
||||
|
||||
/* We are having trouble resyncing ALPS touchpads so disable it for now */
|
||||
psmouse->resync_time = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
init_fail:
|
||||
input_free_device(dev2);
|
||||
kfree(priv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int alps_detect(struct psmouse *psmouse, int set_properties)
|
||||
{
|
||||
int version;
|
||||
const struct alps_model_info *model;
|
||||
|
||||
if (!(model = alps_get_model(psmouse, &version)))
|
||||
return -1;
|
||||
|
||||
if (set_properties) {
|
||||
psmouse->vendor = "ALPS";
|
||||
psmouse->name = model->flags & ALPS_DUALPOINT ?
|
||||
"DualPoint TouchPad" : "GlidePoint";
|
||||
psmouse->model = version;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
32
drivers/input/mouse/alps.h
Normal file
32
drivers/input/mouse/alps.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
int alps_detect(struct psmouse *psmouse, int set_properties);
|
||||
int alps_init(struct psmouse *psmouse);
|
||||
|
||||
struct alps_model_info {
|
||||
unsigned char signature[3];
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
};
|
||||
|
||||
struct alps_data {
|
||||
struct input_dev *dev2; /* Relative device */
|
||||
char name[32]; /* Name */
|
||||
char phys[32]; /* Phys */
|
||||
const struct alps_model_info *i;/* Info */
|
||||
int prev_fin; /* Finger bit from previous packet */
|
||||
};
|
||||
|
||||
#endif
|
||||
135
drivers/input/mouse/amimouse.c
Normal file
135
drivers/input/mouse/amimouse.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 <asm/irq.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/system.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 struct input_dev *amimouse_dev;
|
||||
|
||||
static irqreturn_t amimouse_interrupt(int irq, void *dummy)
|
||||
{
|
||||
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(amimouse_dev, REL_X, dx);
|
||||
input_report_rel(amimouse_dev, REL_Y, dy);
|
||||
|
||||
input_report_key(amimouse_dev, BTN_LEFT, ciaa.pra & 0x40);
|
||||
input_report_key(amimouse_dev, BTN_MIDDLE, potgor & 0x0100);
|
||||
input_report_key(amimouse_dev, BTN_RIGHT, potgor & 0x0400);
|
||||
|
||||
input_sync(amimouse_dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int amimouse_open(struct input_dev *dev)
|
||||
{
|
||||
unsigned short joy0dat;
|
||||
|
||||
joy0dat = amiga_custom.joy0dat;
|
||||
|
||||
amimouse_lastx = joy0dat & 0xff;
|
||||
amimouse_lasty = joy0dat >> 8;
|
||||
|
||||
if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) {
|
||||
printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amimouse_close(struct input_dev *dev)
|
||||
{
|
||||
free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt);
|
||||
}
|
||||
|
||||
static int __init amimouse_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
|
||||
return -ENODEV;
|
||||
|
||||
amimouse_dev = input_allocate_device();
|
||||
if (!amimouse_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
amimouse_dev->name = "Amiga mouse";
|
||||
amimouse_dev->phys = "amimouse/input0";
|
||||
amimouse_dev->id.bustype = BUS_AMIGA;
|
||||
amimouse_dev->id.vendor = 0x0001;
|
||||
amimouse_dev->id.product = 0x0002;
|
||||
amimouse_dev->id.version = 0x0100;
|
||||
|
||||
amimouse_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
amimouse_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
amimouse_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
amimouse_dev->open = amimouse_open;
|
||||
amimouse_dev->close = amimouse_close;
|
||||
|
||||
err = input_register_device(amimouse_dev);
|
||||
if (err) {
|
||||
input_free_device(amimouse_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit amimouse_exit(void)
|
||||
{
|
||||
input_unregister_device(amimouse_dev);
|
||||
}
|
||||
|
||||
module_init(amimouse_init);
|
||||
module_exit(amimouse_exit);
|
||||
429
drivers/input/mouse/hil_ptr.c
Normal file
429
drivers/input/mouse/hil_ptr.c
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Generic linux-input device driver for axis-bearing devices
|
||||
*
|
||||
* Copyright (c) 2001 Brian S. Julin
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL").
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
*
|
||||
* References:
|
||||
* HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/hil.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci_ids.h>
|
||||
|
||||
#define PREFIX "HIL PTR: "
|
||||
#define HIL_GENERIC_NAME "HIL pointer device"
|
||||
|
||||
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
|
||||
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
|
||||
#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
|
||||
#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */
|
||||
|
||||
|
||||
#define HIL_PTR_MAX_LENGTH 16
|
||||
|
||||
struct hil_ptr {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
|
||||
/* Input buffer and index for packets from HIL bus. */
|
||||
hil_packet data[HIL_PTR_MAX_LENGTH];
|
||||
int idx4; /* four counts per packet */
|
||||
|
||||
/* Raw device info records from HIL bus, see hil.h for fields. */
|
||||
char idd[HIL_PTR_MAX_LENGTH]; /* DID byte and IDD record */
|
||||
char rsc[HIL_PTR_MAX_LENGTH]; /* RSC record */
|
||||
char exd[HIL_PTR_MAX_LENGTH]; /* EXD record */
|
||||
char rnm[HIL_PTR_MAX_LENGTH + 1]; /* RNM record + NULL term. */
|
||||
|
||||
/* Extra device details not contained in struct input_dev. */
|
||||
unsigned int nbtn, naxes;
|
||||
unsigned int btnmap[7];
|
||||
|
||||
/* Something to sleep around with. */
|
||||
struct semaphore sem;
|
||||
};
|
||||
|
||||
/* Process a complete packet after transfer from the HIL */
|
||||
static void hil_ptr_process_record(struct hil_ptr *ptr)
|
||||
{
|
||||
struct input_dev *dev = ptr->dev;
|
||||
hil_packet *data = ptr->data;
|
||||
hil_packet p;
|
||||
int idx, i, cnt, laxis;
|
||||
int ax16, absdev;
|
||||
|
||||
idx = ptr->idx4/4;
|
||||
p = data[idx - 1];
|
||||
|
||||
if ((p & ~HIL_CMDCT_POL) ==
|
||||
(HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
|
||||
if ((p & ~HIL_CMDCT_RPL) ==
|
||||
(HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
|
||||
|
||||
/* Not a poll response. See if we are loading config records. */
|
||||
switch (p & HIL_PKT_DATA_MASK) {
|
||||
case HIL_CMD_IDD:
|
||||
for (i = 0; i < idx; i++)
|
||||
ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_PTR_MAX_LENGTH; i++)
|
||||
ptr->idd[i] = 0;
|
||||
break;
|
||||
case HIL_CMD_RSC:
|
||||
for (i = 0; i < idx; i++)
|
||||
ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_PTR_MAX_LENGTH; i++)
|
||||
ptr->rsc[i] = 0;
|
||||
break;
|
||||
case HIL_CMD_EXD:
|
||||
for (i = 0; i < idx; i++)
|
||||
ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_PTR_MAX_LENGTH; i++)
|
||||
ptr->exd[i] = 0;
|
||||
break;
|
||||
case HIL_CMD_RNM:
|
||||
for (i = 0; i < idx; i++)
|
||||
ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_PTR_MAX_LENGTH + 1; i++)
|
||||
ptr->rnm[i] = '\0';
|
||||
break;
|
||||
default:
|
||||
/* These occur when device isn't present */
|
||||
if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break;
|
||||
/* Anything else we'd like to know about. */
|
||||
printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
|
||||
report:
|
||||
if ((p & HIL_CMDCT_POL) != idx - 1) {
|
||||
printk(KERN_WARNING PREFIX "Malformed poll packet %x (idx = %i)\n", p, idx);
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0;
|
||||
laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK;
|
||||
laxis += i;
|
||||
|
||||
ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
|
||||
absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
|
||||
|
||||
for (cnt = 1; i < laxis; i++) {
|
||||
unsigned int lo,hi,val;
|
||||
lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
|
||||
hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
|
||||
if (absdev) {
|
||||
val = lo + (hi<<8);
|
||||
#ifdef TABLET_AUTOADJUST
|
||||
if (val < dev->absmin[ABS_X + i])
|
||||
dev->absmin[ABS_X + i] = val;
|
||||
if (val > dev->absmax[ABS_X + i])
|
||||
dev->absmax[ABS_X + i] = val;
|
||||
#endif
|
||||
if (i%3) val = dev->absmax[ABS_X + i] - val;
|
||||
input_report_abs(dev, ABS_X + i, val);
|
||||
} else {
|
||||
val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
|
||||
if (i%3) val *= -1;
|
||||
input_report_rel(dev, REL_X + i, val);
|
||||
}
|
||||
}
|
||||
|
||||
while (cnt < idx - 1) {
|
||||
unsigned int btn;
|
||||
int up;
|
||||
btn = ptr->data[cnt++];
|
||||
up = btn & 1;
|
||||
btn &= 0xfe;
|
||||
if (btn == 0x8e) {
|
||||
continue; /* TODO: proximity == touch? */
|
||||
}
|
||||
else if ((btn > 0x8c) || (btn < 0x80)) continue;
|
||||
btn = (btn - 0x80) >> 1;
|
||||
btn = ptr->btnmap[btn];
|
||||
input_report_key(dev, btn, !up);
|
||||
}
|
||||
input_sync(dev);
|
||||
out:
|
||||
ptr->idx4 = 0;
|
||||
up(&ptr->sem);
|
||||
}
|
||||
|
||||
static void hil_ptr_process_err(struct hil_ptr *ptr) {
|
||||
printk(KERN_WARNING PREFIX "errored HIL packet\n");
|
||||
ptr->idx4 = 0;
|
||||
up(&ptr->sem);
|
||||
return;
|
||||
}
|
||||
|
||||
static irqreturn_t hil_ptr_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct hil_ptr *ptr;
|
||||
hil_packet packet;
|
||||
int idx;
|
||||
|
||||
ptr = serio_get_drvdata(serio);
|
||||
if (ptr == NULL) {
|
||||
BUG();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) {
|
||||
hil_ptr_process_err(ptr);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
idx = ptr->idx4/4;
|
||||
if (!(ptr->idx4 % 4)) ptr->data[idx] = 0;
|
||||
packet = ptr->data[idx];
|
||||
packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8);
|
||||
ptr->data[idx] = packet;
|
||||
|
||||
/* Records of N 4-byte hil_packets must terminate with a command. */
|
||||
if ((++(ptr->idx4)) % 4) return IRQ_HANDLED;
|
||||
if ((packet & 0xffff0000) != HIL_ERR_INT) {
|
||||
hil_ptr_process_err(ptr);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (packet & HIL_PKT_CMD)
|
||||
hil_ptr_process_record(ptr);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void hil_ptr_disconnect(struct serio *serio)
|
||||
{
|
||||
struct hil_ptr *ptr;
|
||||
|
||||
ptr = serio_get_drvdata(serio);
|
||||
if (ptr == NULL) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
serio_close(serio);
|
||||
input_unregister_device(ptr->dev);
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
|
||||
{
|
||||
struct hil_ptr *ptr;
|
||||
char *txt;
|
||||
unsigned int i, naxsets, btntype;
|
||||
uint8_t did, *idd;
|
||||
|
||||
if (!(ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
ptr->dev = input_allocate_device();
|
||||
if (!ptr->dev)
|
||||
goto bail0;
|
||||
|
||||
ptr->dev->private = ptr;
|
||||
|
||||
if (serio_open(serio, driver))
|
||||
goto bail1;
|
||||
|
||||
serio_set_drvdata(serio, ptr);
|
||||
ptr->serio = serio;
|
||||
|
||||
init_MUTEX_LOCKED(&(ptr->sem));
|
||||
|
||||
/* Get device info. MLC driver supplies devid/status/etc. */
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_IDD);
|
||||
down(&(ptr->sem));
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_RSC);
|
||||
down(&(ptr->sem));
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_RNM);
|
||||
down(&(ptr->sem));
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_EXD);
|
||||
down(&(ptr->sem));
|
||||
|
||||
up(&(ptr->sem));
|
||||
|
||||
did = ptr->idd[0];
|
||||
idd = ptr->idd + 1;
|
||||
txt = "unknown";
|
||||
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
|
||||
ptr->dev->evbit[0] = BIT(EV_REL);
|
||||
txt = "relative";
|
||||
}
|
||||
|
||||
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) {
|
||||
ptr->dev->evbit[0] = BIT(EV_ABS);
|
||||
txt = "absolute";
|
||||
}
|
||||
if (!ptr->dev->evbit[0]) {
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
|
||||
if (ptr->nbtn) ptr->dev->evbit[0] |= BIT(EV_KEY);
|
||||
|
||||
naxsets = HIL_IDD_NUM_AXSETS(*idd);
|
||||
ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
|
||||
|
||||
printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n",
|
||||
did, txt);
|
||||
printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n",
|
||||
ptr->nbtn, naxsets, ptr->naxes);
|
||||
|
||||
btntype = BTN_MISC;
|
||||
if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
|
||||
#ifdef TABLET_SIMULATES_MOUSE
|
||||
btntype = BTN_TOUCH;
|
||||
#else
|
||||
btntype = BTN_DIGI;
|
||||
#endif
|
||||
if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
|
||||
btntype = BTN_TOUCH;
|
||||
|
||||
if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
|
||||
btntype = BTN_MOUSE;
|
||||
|
||||
for (i = 0; i < ptr->nbtn; i++) {
|
||||
set_bit(btntype | i, ptr->dev->keybit);
|
||||
ptr->btnmap[i] = btntype | i;
|
||||
}
|
||||
|
||||
if (btntype == BTN_MOUSE) {
|
||||
/* Swap buttons 2 and 3 */
|
||||
ptr->btnmap[1] = BTN_MIDDLE;
|
||||
ptr->btnmap[2] = BTN_RIGHT;
|
||||
}
|
||||
|
||||
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
|
||||
for (i = 0; i < ptr->naxes; i++) {
|
||||
set_bit(REL_X + i, ptr->dev->relbit);
|
||||
}
|
||||
for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
|
||||
set_bit(REL_X + i, ptr->dev->relbit);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < ptr->naxes; i++) {
|
||||
set_bit(ABS_X + i, ptr->dev->absbit);
|
||||
ptr->dev->absmin[ABS_X + i] = 0;
|
||||
ptr->dev->absmax[ABS_X + i] =
|
||||
HIL_IDD_AXIS_MAX((ptr->idd + 1), i);
|
||||
}
|
||||
for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
|
||||
set_bit(ABS_X + i, ptr->dev->absbit);
|
||||
ptr->dev->absmin[ABS_X + i] = 0;
|
||||
ptr->dev->absmax[ABS_X + i] =
|
||||
HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3));
|
||||
}
|
||||
#ifdef TABLET_AUTOADJUST
|
||||
for (i = 0; i < ABS_MAX; i++) {
|
||||
int diff = ptr->dev->absmax[ABS_X + i] / 10;
|
||||
ptr->dev->absmin[ABS_X + i] += diff;
|
||||
ptr->dev->absmax[ABS_X + i] -= diff;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
|
||||
|
||||
ptr->dev->id.bustype = BUS_HIL;
|
||||
ptr->dev->id.vendor = PCI_VENDOR_ID_HP;
|
||||
ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */
|
||||
ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */
|
||||
ptr->dev->cdev.dev = &serio->dev;
|
||||
|
||||
input_register_device(ptr->dev);
|
||||
printk(KERN_INFO "input: %s (%s), ID: %d\n",
|
||||
ptr->dev->name,
|
||||
(btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
|
||||
did);
|
||||
|
||||
return 0;
|
||||
bail2:
|
||||
serio_close(serio);
|
||||
bail1:
|
||||
input_free_device(ptr->dev);
|
||||
bail0:
|
||||
kfree(ptr);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct serio_device_id hil_ptr_ids[] = {
|
||||
{
|
||||
.type = SERIO_HIL_MLC,
|
||||
.proto = SERIO_HIL,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct serio_driver hil_ptr_serio_driver = {
|
||||
.driver = {
|
||||
.name = "hil_ptr",
|
||||
},
|
||||
.description = "HP HIL mouse/tablet driver",
|
||||
.id_table = hil_ptr_ids,
|
||||
.connect = hil_ptr_connect,
|
||||
.disconnect = hil_ptr_disconnect,
|
||||
.interrupt = hil_ptr_interrupt
|
||||
};
|
||||
|
||||
static int __init hil_ptr_init(void)
|
||||
{
|
||||
return serio_register_driver(&hil_ptr_serio_driver);
|
||||
}
|
||||
|
||||
static void __exit hil_ptr_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&hil_ptr_serio_driver);
|
||||
}
|
||||
|
||||
module_init(hil_ptr_init);
|
||||
module_exit(hil_ptr_exit);
|
||||
198
drivers/input/mouse/inport.c
Normal file
198
drivers/input/mouse/inport.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* $Id: inport.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* 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/moduleparam.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_ERR "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(EV_KEY) | BIT(EV_REL);
|
||||
inport_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
inport_dev->relbit[0] = BIT(REL_X) | BIT(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);
|
||||
171
drivers/input/mouse/lifebook.c
Normal file
171
drivers/input/mouse/lifebook.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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 "psmouse.h"
|
||||
#include "lifebook.h"
|
||||
|
||||
static struct dmi_system_id lifebook_dmi_table[] = {
|
||||
{
|
||||
.ident = "FLORA-ie 55mi",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "LifeBook B",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lifebook B",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lifebook B213x/B2150",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Zephyr",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "CF-18",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lifebook B142",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
|
||||
if (psmouse->pktcnt != 3)
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
|
||||
/* calculate X and Y */
|
||||
if ((packet[0] & 0x08) == 0x00) {
|
||||
input_report_abs(dev, ABS_X,
|
||||
(packet[1] | ((packet[0] & 0x30) << 4)));
|
||||
input_report_abs(dev, ABS_Y,
|
||||
1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
|
||||
} else {
|
||||
input_report_rel(dev, REL_X,
|
||||
((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
|
||||
input_report_rel(dev, REL_Y,
|
||||
-(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
|
||||
}
|
||||
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
|
||||
input_report_key(dev, BTN_TOUCH, packet[0] & 0x04);
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
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 touchsreen will never send
|
||||
absolute coordinates
|
||||
*/
|
||||
param = 0x07;
|
||||
ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
psmouse_reset(psmouse);
|
||||
}
|
||||
|
||||
int lifebook_detect(struct psmouse *psmouse, int set_properties)
|
||||
{
|
||||
if (!dmi_check_system(lifebook_dmi_table))
|
||||
return -1;
|
||||
|
||||
if (set_properties) {
|
||||
psmouse->vendor = "Fujitsu";
|
||||
psmouse->name = "Lifebook TouchScreen";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lifebook_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *input_dev = psmouse->dev;
|
||||
|
||||
if (lifebook_absolute_mode(psmouse))
|
||||
return -1;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL);
|
||||
input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 1024, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 1024, 0, 0);
|
||||
|
||||
psmouse->protocol_handler = lifebook_process_byte;
|
||||
psmouse->set_resolution = lifebook_set_resolution;
|
||||
psmouse->disconnect = lifebook_disconnect;
|
||||
psmouse->reconnect = lifebook_absolute_mode;
|
||||
psmouse->pktsize = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
17
drivers/input/mouse/lifebook.h
Normal file
17
drivers/input/mouse/lifebook.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
int lifebook_detect(struct psmouse *psmouse, int set_properties);
|
||||
int lifebook_init(struct psmouse *psmouse);
|
||||
|
||||
#endif
|
||||
187
drivers/input/mouse/logibm.c
Normal file
187
drivers/input/mouse/logibm.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* $Id: logibm.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* 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/moduleparam.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_ERR "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(EV_KEY) | BIT(EV_REL);
|
||||
logibm_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
logibm_dev->relbit[0] = BIT(REL_X) | BIT(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);
|
||||
417
drivers/input/mouse/logips2pp.c
Normal file
417
drivers/input/mouse/logips2pp.c
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
#ifdef DEBUG
|
||||
default:
|
||||
printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
|
||||
(packet[1] >> 4) | (packet[0] & 0x30));
|
||||
#endif
|
||||
}
|
||||
} 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, unsigned int smartscroll)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
unsigned char param[4];
|
||||
|
||||
if (smartscroll > 1)
|
||||
smartscroll = 1;
|
||||
|
||||
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 ? 1 : 0);
|
||||
}
|
||||
|
||||
static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || 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, ¶m, 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[] = {
|
||||
{ 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 },
|
||||
{ 73, 0, PS2PP_SIDE_BTN },
|
||||
{ 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];
|
||||
|
||||
printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
|
||||
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,
|
||||
int 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, int set_properties)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
unsigned char param[4];
|
||||
unsigned char model, buttons;
|
||||
const struct ps2pp_info *model_info;
|
||||
int use_ps2pp = 0;
|
||||
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);
|
||||
|
||||
if (!param[1])
|
||||
return -1;
|
||||
|
||||
model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
|
||||
buttons = param[1];
|
||||
|
||||
if ((model_info = get_model_info(model)) != NULL) {
|
||||
|
||||
/*
|
||||
* 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 = 1;
|
||||
}
|
||||
|
||||
} 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, psmouse->smartscroll);
|
||||
use_ps2pp = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
printk(KERN_ERR
|
||||
"logips2pp.c: failed to create smartscroll "
|
||||
"sysfs attribute, error: %d\n", error);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons < 3)
|
||||
clear_bit(BTN_MIDDLE, psmouse->dev->keybit);
|
||||
|
||||
if (model_info)
|
||||
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
|
||||
}
|
||||
|
||||
return use_ps2pp ? 0 : -1;
|
||||
}
|
||||
|
||||
16
drivers/input/mouse/logips2pp.h
Normal file
16
drivers/input/mouse/logips2pp.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
int ps2pp_init(struct psmouse *psmouse, int set_properties);
|
||||
|
||||
#endif
|
||||
182
drivers/input/mouse/pc110pad.c
Normal file
182
drivers/input/mouse/pc110pad.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* $Id: pc110pad.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* 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 <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_p(handshake | 1, pc110pad_io + 2);
|
||||
outb_p(handshake & ~1, pc110pad_io + 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)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
int err;
|
||||
|
||||
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
|
||||
if (dev) {
|
||||
pci_dev_put(dev);
|
||||
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(EV_KEY) | BIT(EV_ABS);
|
||||
pc110pad_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
|
||||
pc110pad_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
pc110pad_dev->absmax[ABS_X] = 0x1ff;
|
||||
pc110pad_dev->absmax[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);
|
||||
1572
drivers/input/mouse/psmouse-base.c
Normal file
1572
drivers/input/mouse/psmouse-base.c
Normal file
File diff suppressed because it is too large
Load Diff
130
drivers/input/mouse/psmouse.h
Normal file
130
drivers/input/mouse/psmouse.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#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_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 work_struct resync_work;
|
||||
char *vendor;
|
||||
char *name;
|
||||
unsigned char packet[8];
|
||||
unsigned char badbyte;
|
||||
unsigned char pktcnt;
|
||||
unsigned char pktsize;
|
||||
unsigned char type;
|
||||
unsigned char acks_disable_command;
|
||||
unsigned int model;
|
||||
unsigned long last;
|
||||
unsigned long out_of_sync;
|
||||
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;
|
||||
unsigned int 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_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
|
||||
int psmouse_reset(struct psmouse *psmouse);
|
||||
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
|
||||
|
||||
|
||||
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);
|
||||
};
|
||||
#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(_name, _mode, _data, _show, _set) \
|
||||
static ssize_t _show(struct psmouse *, void *data, char *); \
|
||||
static ssize_t _set(struct psmouse *, void *data, const char *, size_t); \
|
||||
static struct psmouse_attribute psmouse_attr_##_name = { \
|
||||
.dattr = { \
|
||||
.attr = { \
|
||||
.name = __stringify(_name), \
|
||||
.mode = _mode, \
|
||||
.owner = THIS_MODULE, \
|
||||
}, \
|
||||
.show = psmouse_attr_show_helper, \
|
||||
.store = psmouse_attr_set_helper, \
|
||||
}, \
|
||||
.data = _data, \
|
||||
.show = _show, \
|
||||
.set = _set, \
|
||||
}
|
||||
|
||||
#endif /* _PSMOUSE_H */
|
||||
115
drivers/input/mouse/rpcmouse.c
Normal file
115
drivers/input/mouse/rpcmouse.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 <asm/hardware.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.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(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(EV_KEY) | BIT(EV_REL);
|
||||
rpcmouse_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
rpcmouse_dev->relbit[0] = BIT(REL_X) | BIT(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);
|
||||
363
drivers/input/mouse/sermouse.c
Normal file
363
drivers/input/mouse/sermouse.c
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* $Id: sermouse.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* 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;
|
||||
|
||||
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->cdev.dev = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
|
||||
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
input_dev->private = sermouse;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int __init sermouse_init(void)
|
||||
{
|
||||
return serio_register_driver(&sermouse_drv);
|
||||
}
|
||||
|
||||
static void __exit sermouse_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&sermouse_drv);
|
||||
}
|
||||
|
||||
module_init(sermouse_init);
|
||||
module_exit(sermouse_exit);
|
||||
683
drivers/input/mouse/synaptics.c
Normal file
683
drivers/input/mouse/synaptics.c
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* Synaptics TouchPad PS/2 mouse driver
|
||||
*
|
||||
* 2003 Dmitry Torokhov <dtor@mail.ru>
|
||||
* Added support for pass-through port. Special thanks to Peter Berg Larsen
|
||||
* for explaining various Synaptics quirks.
|
||||
*
|
||||
* 2003 Peter Osterlund <petero2@telia.com>
|
||||
* Ported to 2.5 input device infrastructure.
|
||||
*
|
||||
* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
|
||||
* start merging tpconfig and gpm code to a xfree-input module
|
||||
* adding some changes and extensions (ex. 3rd and 4th button)
|
||||
*
|
||||
* Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
|
||||
* Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
|
||||
* code for the special synaptics commands (from the tpconfig-source)
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/libps2.h>
|
||||
#include "psmouse.h"
|
||||
#include "synaptics.h"
|
||||
|
||||
/*
|
||||
* The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
|
||||
* section 2.3.2, which says that they should be valid regardless of the
|
||||
* actual size of the sensor.
|
||||
*/
|
||||
#define XMIN_NOMINAL 1472
|
||||
#define XMAX_NOMINAL 5472
|
||||
#define YMIN_NOMINAL 1408
|
||||
#define YMAX_NOMINAL 4448
|
||||
|
||||
/*****************************************************************************
|
||||
* Synaptics communications functions
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* Send a command to the synpatics touchpad by special commands
|
||||
*/
|
||||
static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
|
||||
{
|
||||
if (psmouse_sliced_command(psmouse, c))
|
||||
return -1;
|
||||
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the synaptics touchpad mode byte by special commands
|
||||
*/
|
||||
static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
|
||||
{
|
||||
unsigned char param[1];
|
||||
|
||||
if (psmouse_sliced_command(psmouse, mode))
|
||||
return -1;
|
||||
param[0] = SYN_PS_SET_MODE2;
|
||||
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the model-id bytes from the touchpad
|
||||
* see also SYN_MODEL_* macros
|
||||
*/
|
||||
static int synaptics_model_id(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
unsigned char mi[3];
|
||||
|
||||
if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
|
||||
return -1;
|
||||
priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the capability-bits from the touchpad
|
||||
* see also the SYN_CAP_* macros
|
||||
*/
|
||||
static int synaptics_capability(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
unsigned char cap[3];
|
||||
|
||||
if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
|
||||
return -1;
|
||||
priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
|
||||
priv->ext_cap = 0;
|
||||
if (!SYN_CAP_VALID(priv->capabilities))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Unless capExtended is set the rest of the flags should be ignored
|
||||
*/
|
||||
if (!SYN_CAP_EXTENDED(priv->capabilities))
|
||||
priv->capabilities = 0;
|
||||
|
||||
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
|
||||
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
|
||||
printk(KERN_ERR "Synaptics claims to have extended capabilities,"
|
||||
" but I'm not able to read them.");
|
||||
} else {
|
||||
priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
|
||||
|
||||
/*
|
||||
* if nExtBtn is greater than 8 it should be considered
|
||||
* invalid and treated as 0
|
||||
*/
|
||||
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
|
||||
priv->ext_cap &= 0xff0fff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Identify Touchpad
|
||||
* See also the SYN_ID_* macros
|
||||
*/
|
||||
static int synaptics_identify(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
unsigned char id[3];
|
||||
|
||||
if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
|
||||
return -1;
|
||||
priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
|
||||
if (SYN_ID_IS_SYNAPTICS(priv->identity))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int synaptics_query_hardware(struct psmouse *psmouse)
|
||||
{
|
||||
int retries = 0;
|
||||
|
||||
while ((retries++ < 3) && psmouse_reset(psmouse))
|
||||
printk(KERN_ERR "synaptics reset failed\n");
|
||||
|
||||
if (synaptics_identify(psmouse))
|
||||
return -1;
|
||||
if (synaptics_model_id(psmouse))
|
||||
return -1;
|
||||
if (synaptics_capability(psmouse))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int synaptics_set_absolute_mode(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
|
||||
priv->mode = SYN_BIT_ABSOLUTE_MODE;
|
||||
if (SYN_ID_MAJOR(priv->identity) >= 4)
|
||||
priv->mode |= SYN_BIT_DISABLE_GESTURE;
|
||||
if (SYN_CAP_EXTENDED(priv->capabilities))
|
||||
priv->mode |= SYN_BIT_W_MODE;
|
||||
|
||||
if (synaptics_mode_cmd(psmouse, priv->mode))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
|
||||
if (rate >= 80) {
|
||||
priv->mode |= SYN_BIT_HIGH_RATE;
|
||||
psmouse->rate = 80;
|
||||
} else {
|
||||
priv->mode &= ~SYN_BIT_HIGH_RATE;
|
||||
psmouse->rate = 40;
|
||||
}
|
||||
|
||||
synaptics_mode_cmd(psmouse, priv->mode);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Synaptics pass-through PS/2 port support
|
||||
****************************************************************************/
|
||||
static int synaptics_pt_write(struct serio *serio, unsigned char c)
|
||||
{
|
||||
struct psmouse *parent = serio_get_drvdata(serio->parent);
|
||||
char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
|
||||
|
||||
if (psmouse_sliced_command(parent, c))
|
||||
return -1;
|
||||
if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int synaptics_is_pt_packet(unsigned char *buf)
|
||||
{
|
||||
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
|
||||
}
|
||||
|
||||
static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
|
||||
{
|
||||
struct psmouse *child = serio_get_drvdata(ptport);
|
||||
|
||||
if (child && child->state == PSMOUSE_ACTIVATED) {
|
||||
serio_interrupt(ptport, packet[1], 0);
|
||||
serio_interrupt(ptport, packet[4], 0);
|
||||
serio_interrupt(ptport, packet[5], 0);
|
||||
if (child->pktsize == 4)
|
||||
serio_interrupt(ptport, packet[2], 0);
|
||||
} else
|
||||
serio_interrupt(ptport, packet[1], 0);
|
||||
}
|
||||
|
||||
static void synaptics_pt_activate(struct psmouse *psmouse)
|
||||
{
|
||||
struct serio *ptport = psmouse->ps2dev.serio->child;
|
||||
struct psmouse *child = serio_get_drvdata(ptport);
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
|
||||
/* adjust the touchpad to child's choice of protocol */
|
||||
if (child) {
|
||||
if (child->pktsize == 4)
|
||||
priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
|
||||
else
|
||||
priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
|
||||
|
||||
if (synaptics_mode_cmd(psmouse, priv->mode))
|
||||
printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void synaptics_pt_create(struct psmouse *psmouse)
|
||||
{
|
||||
struct serio *serio;
|
||||
|
||||
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
|
||||
if (!serio) {
|
||||
printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
|
||||
return;
|
||||
}
|
||||
|
||||
serio->id.type = SERIO_PS_PSTHRU;
|
||||
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
||||
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
|
||||
serio->write = synaptics_pt_write;
|
||||
serio->parent = psmouse->ps2dev.serio;
|
||||
|
||||
psmouse->pt_activate = synaptics_pt_activate;
|
||||
|
||||
printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys);
|
||||
serio_register_port(serio);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Functions to interpret the absolute mode packets
|
||||
****************************************************************************/
|
||||
|
||||
static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
|
||||
{
|
||||
memset(hw, 0, sizeof(struct synaptics_hw_state));
|
||||
|
||||
if (SYN_MODEL_NEWABS(priv->model_id)) {
|
||||
hw->x = (((buf[3] & 0x10) << 8) |
|
||||
((buf[1] & 0x0f) << 8) |
|
||||
buf[4]);
|
||||
hw->y = (((buf[3] & 0x20) << 7) |
|
||||
((buf[1] & 0xf0) << 4) |
|
||||
buf[5]);
|
||||
|
||||
hw->z = buf[2];
|
||||
hw->w = (((buf[0] & 0x30) >> 2) |
|
||||
((buf[0] & 0x04) >> 1) |
|
||||
((buf[3] & 0x04) >> 2));
|
||||
|
||||
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
||||
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
||||
|
||||
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
|
||||
hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
|
||||
if (hw->w == 2)
|
||||
hw->scroll = (signed char)(buf[1]);
|
||||
}
|
||||
|
||||
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
|
||||
hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
|
||||
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
|
||||
((buf[0] ^ buf[3]) & 0x02)) {
|
||||
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
|
||||
default:
|
||||
/*
|
||||
* if nExtBtn is greater than 8 it should be
|
||||
* considered invalid and treated as 0
|
||||
*/
|
||||
break;
|
||||
case 8:
|
||||
hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
|
||||
hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
|
||||
case 6:
|
||||
hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
|
||||
hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
|
||||
case 4:
|
||||
hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
|
||||
hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
|
||||
case 2:
|
||||
hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
|
||||
hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
|
||||
hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
|
||||
|
||||
hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
|
||||
hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
|
||||
|
||||
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
||||
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called for each full received packet from the touchpad
|
||||
*/
|
||||
static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
struct synaptics_hw_state hw;
|
||||
int num_fingers;
|
||||
int finger_width;
|
||||
int i;
|
||||
|
||||
synaptics_parse_hw_state(psmouse->packet, priv, &hw);
|
||||
|
||||
if (hw.scroll) {
|
||||
priv->scroll += hw.scroll;
|
||||
|
||||
while (priv->scroll >= 4) {
|
||||
input_report_key(dev, BTN_BACK, !hw.down);
|
||||
input_sync(dev);
|
||||
input_report_key(dev, BTN_BACK, hw.down);
|
||||
input_sync(dev);
|
||||
priv->scroll -= 4;
|
||||
}
|
||||
while (priv->scroll <= -4) {
|
||||
input_report_key(dev, BTN_FORWARD, !hw.up);
|
||||
input_sync(dev);
|
||||
input_report_key(dev, BTN_FORWARD, hw.up);
|
||||
input_sync(dev);
|
||||
priv->scroll += 4;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (hw.z > 0) {
|
||||
num_fingers = 1;
|
||||
finger_width = 5;
|
||||
if (SYN_CAP_EXTENDED(priv->capabilities)) {
|
||||
switch (hw.w) {
|
||||
case 0 ... 1:
|
||||
if (SYN_CAP_MULTIFINGER(priv->capabilities))
|
||||
num_fingers = hw.w + 2;
|
||||
break;
|
||||
case 2:
|
||||
if (SYN_MODEL_PEN(priv->model_id))
|
||||
; /* Nothing, treat a pen as a single finger */
|
||||
break;
|
||||
case 4 ... 15:
|
||||
if (SYN_CAP_PALMDETECT(priv->capabilities))
|
||||
finger_width = hw.w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
num_fingers = 0;
|
||||
finger_width = 0;
|
||||
}
|
||||
|
||||
/* Post events
|
||||
* BTN_TOUCH has to be first as mousedev relies on it when doing
|
||||
* absolute -> relative conversion
|
||||
*/
|
||||
if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
|
||||
if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
|
||||
|
||||
if (hw.z > 0) {
|
||||
input_report_abs(dev, ABS_X, hw.x);
|
||||
input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
|
||||
}
|
||||
input_report_abs(dev, ABS_PRESSURE, hw.z);
|
||||
|
||||
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
|
||||
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
|
||||
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
|
||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, hw.left);
|
||||
input_report_key(dev, BTN_RIGHT, hw.right);
|
||||
|
||||
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
|
||||
input_report_key(dev, BTN_MIDDLE, hw.middle);
|
||||
|
||||
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
|
||||
input_report_key(dev, BTN_FORWARD, hw.up);
|
||||
input_report_key(dev, BTN_BACK, hw.down);
|
||||
}
|
||||
|
||||
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
|
||||
input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
|
||||
{
|
||||
static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
|
||||
static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
|
||||
static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
|
||||
static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
|
||||
static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
|
||||
|
||||
if (idx < 0 || idx > 4)
|
||||
return 0;
|
||||
|
||||
switch (pkt_type) {
|
||||
case SYN_NEWABS:
|
||||
case SYN_NEWABS_RELAXED:
|
||||
return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
|
||||
|
||||
case SYN_NEWABS_STRICT:
|
||||
return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
|
||||
|
||||
case SYN_OLDABS:
|
||||
return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
|
||||
printk(KERN_INFO "synaptics: using relaxed packet validation\n");
|
||||
return SYN_NEWABS_RELAXED;
|
||||
}
|
||||
|
||||
return SYN_NEWABS_STRICT;
|
||||
}
|
||||
|
||||
static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
|
||||
if (psmouse->pktcnt >= 6) { /* Full packet received */
|
||||
if (unlikely(priv->pkt_type == SYN_NEWABS))
|
||||
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
|
||||
|
||||
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
|
||||
if (psmouse->ps2dev.serio->child)
|
||||
synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
|
||||
} else
|
||||
synaptics_process_packet(psmouse);
|
||||
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
|
||||
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Driver initialization/cleanup functions
|
||||
****************************************************************************/
|
||||
static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
set_bit(EV_ABS, dev->evbit);
|
||||
input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
set_bit(ABS_TOOL_WIDTH, dev->absbit);
|
||||
|
||||
set_bit(EV_KEY, dev->evbit);
|
||||
set_bit(BTN_TOUCH, dev->keybit);
|
||||
set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
||||
set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
|
||||
|
||||
set_bit(BTN_LEFT, dev->keybit);
|
||||
set_bit(BTN_RIGHT, dev->keybit);
|
||||
|
||||
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
|
||||
set_bit(BTN_MIDDLE, dev->keybit);
|
||||
|
||||
if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
|
||||
SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
|
||||
set_bit(BTN_FORWARD, dev->keybit);
|
||||
set_bit(BTN_BACK, dev->keybit);
|
||||
}
|
||||
|
||||
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
|
||||
set_bit(BTN_0 + i, dev->keybit);
|
||||
|
||||
clear_bit(EV_REL, dev->evbit);
|
||||
clear_bit(REL_X, dev->relbit);
|
||||
clear_bit(REL_Y, dev->relbit);
|
||||
}
|
||||
|
||||
void synaptics_reset(struct psmouse *psmouse)
|
||||
{
|
||||
/* reset touchpad back to relative mode, gestures enabled */
|
||||
synaptics_mode_cmd(psmouse, 0);
|
||||
}
|
||||
|
||||
static void synaptics_disconnect(struct psmouse *psmouse)
|
||||
{
|
||||
synaptics_reset(psmouse);
|
||||
kfree(psmouse->private);
|
||||
psmouse->private = NULL;
|
||||
}
|
||||
|
||||
static int synaptics_reconnect(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
struct synaptics_data old_priv = *priv;
|
||||
|
||||
if (synaptics_detect(psmouse, 0))
|
||||
return -1;
|
||||
|
||||
if (synaptics_query_hardware(psmouse)) {
|
||||
printk(KERN_ERR "Unable to query Synaptics hardware.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (old_priv.identity != priv->identity ||
|
||||
old_priv.model_id != priv->model_id ||
|
||||
old_priv.capabilities != priv->capabilities ||
|
||||
old_priv.ext_cap != priv->ext_cap)
|
||||
return -1;
|
||||
|
||||
if (synaptics_set_absolute_mode(psmouse)) {
|
||||
printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int synaptics_detect(struct psmouse *psmouse, int set_properties)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
unsigned char param[4];
|
||||
|
||||
param[0] = 0;
|
||||
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
|
||||
|
||||
if (param[1] != 0x47)
|
||||
return -1;
|
||||
|
||||
if (set_properties) {
|
||||
psmouse->vendor = "Synaptics";
|
||||
psmouse->name = "TouchPad";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
#include <linux/dmi.h>
|
||||
static struct dmi_system_id toshiba_dmi_table[] = {
|
||||
{
|
||||
.ident = "Toshiba Satellite",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Toshiba Dynabook",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Toshiba Portege M300",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
int synaptics_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct synaptics_data *priv;
|
||||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -1;
|
||||
|
||||
if (synaptics_query_hardware(psmouse)) {
|
||||
printk(KERN_ERR "Unable to query Synaptics hardware.\n");
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (synaptics_set_absolute_mode(psmouse)) {
|
||||
printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
|
||||
|
||||
printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx\n",
|
||||
SYN_ID_MODEL(priv->identity),
|
||||
SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
|
||||
priv->model_id, priv->capabilities, priv->ext_cap);
|
||||
|
||||
set_input_params(psmouse->dev, priv);
|
||||
|
||||
psmouse->protocol_handler = synaptics_process_byte;
|
||||
psmouse->set_rate = synaptics_set_rate;
|
||||
psmouse->disconnect = synaptics_disconnect;
|
||||
psmouse->reconnect = synaptics_reconnect;
|
||||
psmouse->cleanup = synaptics_reset;
|
||||
psmouse->pktsize = 6;
|
||||
/* Synaptics can usually stay in sync without extra help */
|
||||
psmouse->resync_time = 0;
|
||||
|
||||
if (SYN_CAP_PASS_THROUGH(priv->capabilities))
|
||||
synaptics_pt_create(psmouse);
|
||||
|
||||
#if defined(__i386__)
|
||||
/*
|
||||
* Toshiba's KBC seems to have trouble handling data from
|
||||
* Synaptics as full rate, switch to lower rate which is roughly
|
||||
* thye same as rate of standard PS/2 mouse.
|
||||
*/
|
||||
if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) {
|
||||
printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
|
||||
dmi_get_system_info(DMI_PRODUCT_NAME));
|
||||
psmouse->rate = 40;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
init_fail:
|
||||
kfree(priv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
110
drivers/input/mouse/synaptics.h
Normal file
110
drivers/input/mouse/synaptics.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
extern int synaptics_detect(struct psmouse *psmouse, int set_properties);
|
||||
extern int synaptics_init(struct psmouse *psmouse);
|
||||
extern void synaptics_reset(struct psmouse *psmouse);
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47)
|
||||
#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
|
||||
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
|
||||
|
||||
/* 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_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
|
||||
|
||||
/* 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
|
||||
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
struct synaptics_data {
|
||||
/* Data read from the touchpad */
|
||||
unsigned long int model_id; /* Model-ID */
|
||||
unsigned long int capabilities; /* Capabilities */
|
||||
unsigned long int ext_cap; /* Extended Capabilities */
|
||||
unsigned long int identity; /* Identification */
|
||||
|
||||
unsigned char pkt_type; /* packet type - old, new, etc */
|
||||
unsigned char mode; /* current mode byte */
|
||||
int scroll;
|
||||
};
|
||||
|
||||
#endif /* _SYNAPTICS_H */
|
||||
336
drivers/input/mouse/trackpoint.c
Normal file
336
drivers/input/mouse/trackpoint.c
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* 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/delay.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/libps2.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "psmouse.h"
|
||||
#include "trackpoint.h"
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Trackpoint-specific attributes
|
||||
*/
|
||||
struct trackpoint_attr_data {
|
||||
size_t field_offset;
|
||||
unsigned char command;
|
||||
unsigned char mask;
|
||||
unsigned char inverted;
|
||||
};
|
||||
|
||||
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 long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 255)
|
||||
return -EINVAL;
|
||||
|
||||
*field = value;
|
||||
trackpoint_write(&psmouse->ps2dev, attr->command, value);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#define TRACKPOINT_INT_ATTR(_name, _command) \
|
||||
static struct trackpoint_attr_data trackpoint_attr_##_name = { \
|
||||
.field_offset = offsetof(struct trackpoint_data, _name), \
|
||||
.command = _command, \
|
||||
}; \
|
||||
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 long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || 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) \
|
||||
static struct trackpoint_attr_data trackpoint_attr_##_name = { \
|
||||
.field_offset = offsetof(struct trackpoint_data, _name), \
|
||||
.command = _command, \
|
||||
.mask = _mask, \
|
||||
.inverted = _inv, \
|
||||
}; \
|
||||
PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
|
||||
&trackpoint_attr_##_name, \
|
||||
trackpoint_show_int_attr, trackpoint_set_bit_attr)
|
||||
|
||||
TRACKPOINT_INT_ATTR(sensitivity, TP_SENS);
|
||||
TRACKPOINT_INT_ATTR(speed, TP_SPEED);
|
||||
TRACKPOINT_INT_ATTR(inertia, TP_INERTIA);
|
||||
TRACKPOINT_INT_ATTR(reach, TP_REACH);
|
||||
TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS);
|
||||
TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG);
|
||||
TRACKPOINT_INT_ATTR(thresh, TP_THRESH);
|
||||
TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH);
|
||||
TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME);
|
||||
TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV);
|
||||
|
||||
TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0);
|
||||
TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0);
|
||||
TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int trackpoint_sync(struct psmouse *psmouse)
|
||||
{
|
||||
struct trackpoint_data *tp = psmouse->private;
|
||||
unsigned char toggle;
|
||||
|
||||
/* Disable features that may make device unusable with this driver */
|
||||
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle);
|
||||
if (toggle & TP_MASK_TWOHAND)
|
||||
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND);
|
||||
|
||||
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle);
|
||||
if (toggle & TP_MASK_SOURCE_TAG)
|
||||
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG);
|
||||
|
||||
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle);
|
||||
if (toggle & TP_MASK_MB)
|
||||
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB);
|
||||
|
||||
/* Push the config to the device */
|
||||
trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity);
|
||||
trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia);
|
||||
trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed);
|
||||
|
||||
trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach);
|
||||
trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys);
|
||||
trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag);
|
||||
|
||||
trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh);
|
||||
trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh);
|
||||
|
||||
trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime);
|
||||
trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks);
|
||||
|
||||
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle);
|
||||
if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select)
|
||||
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON);
|
||||
|
||||
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle);
|
||||
if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback)
|
||||
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK);
|
||||
|
||||
trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle);
|
||||
if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev)
|
||||
trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void trackpoint_defaults(struct trackpoint_data *tp)
|
||||
{
|
||||
tp->press_to_select = TP_DEF_PTSON;
|
||||
tp->sensitivity = TP_DEF_SENS;
|
||||
tp->speed = TP_DEF_SPEED;
|
||||
tp->reach = TP_DEF_REACH;
|
||||
|
||||
tp->draghys = TP_DEF_DRAGHYS;
|
||||
tp->mindrag = TP_DEF_MINDRAG;
|
||||
|
||||
tp->thresh = TP_DEF_THRESH;
|
||||
tp->upthresh = TP_DEF_UP_THRESH;
|
||||
|
||||
tp->ztime = TP_DEF_Z_TIME;
|
||||
tp->jenks = TP_DEF_JENKS_CURV;
|
||||
|
||||
tp->inertia = TP_DEF_INERTIA;
|
||||
tp->skipback = TP_DEF_SKIPBACK;
|
||||
tp->ext_dev = TP_DEF_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)
|
||||
{
|
||||
if (trackpoint_start_protocol(psmouse, NULL))
|
||||
return -1;
|
||||
|
||||
if (trackpoint_sync(psmouse))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trackpoint_detect(struct psmouse *psmouse, int set_properties)
|
||||
{
|
||||
struct trackpoint_data *priv;
|
||||
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)) {
|
||||
printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n");
|
||||
button_info = 0;
|
||||
}
|
||||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -1;
|
||||
|
||||
psmouse->vendor = "IBM";
|
||||
psmouse->name = "TrackPoint";
|
||||
|
||||
psmouse->reconnect = trackpoint_reconnect;
|
||||
psmouse->disconnect = trackpoint_disconnect;
|
||||
|
||||
trackpoint_defaults(priv);
|
||||
trackpoint_sync(psmouse);
|
||||
|
||||
error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
|
||||
if (error) {
|
||||
printk(KERN_ERR
|
||||
"trackpoint.c: failed to create sysfs attributes, error: %d\n",
|
||||
error);
|
||||
kfree(priv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
|
||||
firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
147
drivers/input/mouse/trackpoint.h
Normal file
147
drivers/input/mouse/trackpoint.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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 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;
|
||||
|
||||
unsigned char press_to_select;
|
||||
unsigned char skipback;
|
||||
|
||||
unsigned char ext_dev;
|
||||
};
|
||||
|
||||
extern int trackpoint_detect(struct psmouse *psmouse, int set_properties);
|
||||
|
||||
#endif /* _TRACKPOINT_H */
|
||||
588
drivers/input/mouse/vsxxxaa.c
Normal file
588
drivers/input/mouse/vsxxxaa.c
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* 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 */
|
||||
if (packet_len > 1) {
|
||||
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)? 1: 0;
|
||||
middle = (buf[0] & 0x02)? 1: 0;
|
||||
right = (buf[0] & 0x01)? 1: 0;
|
||||
|
||||
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)? 1: 0;
|
||||
middle = (buf[0] & 0x04)? 1: 0;
|
||||
right = (buf[0] & 0x08)? 1: 0;
|
||||
touch = (buf[0] & 0x10)? 1: 0;
|
||||
|
||||
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 explicitely
|
||||
* 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 occured. 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)? 1: 0;
|
||||
middle = (buf[0] & 0x02)? 1: 0;
|
||||
right = (buf[0] & 0x01)? 1: 0;
|
||||
|
||||
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: Forceing standard packet format, "
|
||||
"incremental streaming mode and 72 samples/sec\n",
|
||||
mouse->name, mouse->phys);
|
||||
mouse->serio->write (mouse->serio, 'S'); /* Standard format */
|
||||
mdelay (50);
|
||||
mouse->serio->write (mouse->serio, 'R'); /* Incremental */
|
||||
mdelay (50);
|
||||
mouse->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 > 0) {
|
||||
printk (KERN_ERR "Dropping %d bytes now...\n",
|
||||
stray_bytes);
|
||||
vsxxxaa_drop_bytes (mouse, stray_bytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
vsxxxaa_handle_REL_packet (mouse);
|
||||
continue; /* More to parse? */
|
||||
}
|
||||
|
||||
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
|
||||
/* Check for broken packet */
|
||||
stray_bytes = vsxxxaa_check_packet (mouse, 5);
|
||||
if (stray_bytes > 0) {
|
||||
printk (KERN_ERR "Dropping %d bytes now...\n",
|
||||
stray_bytes);
|
||||
vsxxxaa_drop_bytes (mouse, stray_bytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
vsxxxaa_handle_ABS_packet (mouse);
|
||||
continue; /* More to parse? */
|
||||
}
|
||||
|
||||
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
|
||||
/* Check for broken packet */
|
||||
stray_bytes = vsxxxaa_check_packet (mouse, 4);
|
||||
if (stray_bytes > 0) {
|
||||
printk (KERN_ERR "Dropping %d bytes now...\n",
|
||||
stray_bytes);
|
||||
vsxxxaa_drop_bytes (mouse, stray_bytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
vsxxxaa_handle_POR_packet (mouse);
|
||||
continue; /* More to parse? */
|
||||
}
|
||||
|
||||
break; /* No REL, ABS or POR packet found */
|
||||
} 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->cdev.dev = &serio->dev;
|
||||
input_dev->private = mouse;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int __init
|
||||
vsxxxaa_init (void)
|
||||
{
|
||||
return serio_register_driver(&vsxxxaa_drv);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
vsxxxaa_exit (void)
|
||||
{
|
||||
serio_unregister_driver(&vsxxxaa_drv);
|
||||
}
|
||||
|
||||
module_init (vsxxxaa_init);
|
||||
module_exit (vsxxxaa_exit);
|
||||
|
||||
Reference in New Issue
Block a user