Input: add new driver for Sentelic Finger Sensing Pad

This is the driver for Sentelic Finger Sensing Pad which can be found
on MSI WIND Netbook.

Signed-off-by: Tai-hwa Liang <avatar@sentelic.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
Tai-hwa Liang 2009-05-10 18:15:39 -07:00 committed by Dmitry Torokhov
parent 3b72094409
commit fc69f4a6af
9 changed files with 1488 additions and 4 deletions

View file

@ -0,0 +1,475 @@
Copyright (C) 2002-2008 Sentelic Corporation.
Last update: Oct-31-2008
==============================================================================
* Finger Sensing Pad Intellimouse Mode(scrolling wheel, 4th and 5th buttons)
==============================================================================
A) MSID 4: Scrolling wheel mode plus Forward page(4th button) and Backward
page (5th button)
@1. Set sample rate to 200;
@2. Set sample rate to 200;
@3. Set sample rate to 80;
@4. Issuing the "Get device ID" command (0xF2) and waits for the response;
@5. FSP will respond 0x04.
Packet 1
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|W|W|W|W|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7 => Y overflow
Bit6 => X overflow
Bit5 => Y sign bit
Bit4 => X sign bit
Bit3 => 1
Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
Bit1 => Right Button, 1 is pressed, 0 is not pressed.
Bit0 => Left Button, 1 is pressed, 0 is not pressed.
Byte 2: X Movement(9-bit 2's complement integers)
Byte 3: Y Movement(9-bit 2's complement integers)
Byte 4: Bit3~Bit0 => the scrolling wheel's movement since the last data report.
valid values, -8 ~ +7
Bit4 => 1 = 4th mouse button is pressed, Forward one page.
0 = 4th mouse button is not pressed.
Bit5 => 1 = 5th mouse button is pressed, Backward one page.
0 = 5th mouse button is not pressed.
B) MSID 6: Horizontal and Vertical scrolling.
@ Set bit 1 in register 0x40 to 1
# FSP replaces scrolling wheel's movement as 4 bits to show horizontal and
vertical scrolling.
Packet 1
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|l|r|u|d|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7 => Y overflow
Bit6 => X overflow
Bit5 => Y sign bit
Bit4 => X sign bit
Bit3 => 1
Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
Bit1 => Right Button, 1 is pressed, 0 is not pressed.
Bit0 => Left Button, 1 is pressed, 0 is not pressed.
Byte 2: X Movement(9-bit 2's complement integers)
Byte 3: Y Movement(9-bit 2's complement integers)
Byte 4: Bit0 => the Vertical scrolling movement downward.
Bit1 => the Vertical scrolling movement upward.
Bit2 => the Vertical scrolling movement rightward.
Bit3 => the Vertical scrolling movement leftward.
Bit4 => 1 = 4th mouse button is pressed, Forward one page.
0 = 4th mouse button is not pressed.
Bit5 => 1 = 5th mouse button is pressed, Backward one page.
0 = 5th mouse button is not pressed.
C) MSID 7:
# FSP uses 2 packets(8 Bytes) data to represent Absolute Position
so we have PACKET NUMBER to identify packets.
If PACKET NUMBER is 0, the packet is Packet 1.
If PACKET NUMBER is 1, the packet is Packet 2.
Please count this number in program.
# MSID6 special packet will be enable at the same time when enable MSID 7.
==============================================================================
* Absolute position for STL3886-G0.
==============================================================================
@ Set bit 2 or 3 in register 0x40 to 1
@ Set bit 6 in register 0x40 to 1
Packet 1 (ABSOLUTE POSITION)
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |0|1|V|1|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |r|l|d|u|X|X|Y|Y|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7~Bit6 => 00, Normal data packet
=> 01, Absolute coordination packet
=> 10, Notify packet
Bit5 => valid bit
Bit4 => 1
Bit3 => 1
Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
Bit1 => Right Button, 1 is pressed, 0 is not pressed.
Bit0 => Left Button, 1 is pressed, 0 is not pressed.
Byte 2: X coordinate (xpos[9:2])
Byte 3: Y coordinate (ypos[9:2])
Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0])
Bit3~Bit2 => X coordinate (ypos[1:0])
Bit4 => scroll up
Bit5 => scroll down
Bit6 => scroll left
Bit7 => scroll right
Notify Packet for G0
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |1|0|0|1|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |M|M|M|M|M|M|M|M| 4 |0|0|0|0|0|0|0|0|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7~Bit6 => 00, Normal data packet
=> 01, Absolute coordination packet
=> 10, Notify packet
Bit5 => 0
Bit4 => 1
Bit3 => 1
Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
Bit1 => Right Button, 1 is pressed, 0 is not pressed.
Bit0 => Left Button, 1 is pressed, 0 is not pressed.
Byte 2: Message Type => 0x5A (Enable/Disable status packet)
Mode Type => 0xA5 (Normal/Icon mode status)
Byte 3: Message Type => 0x00 (Disabled)
=> 0x01 (Enabled)
Mode Type => 0x00 (Normal)
=> 0x01 (Icon)
Byte 4: Bit7~Bit0 => Don't Care
==============================================================================
* Absolute position for STL3888-A0.
==============================================================================
Packet 1 (ABSOLUTE POSITION)
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |0|1|V|A|1|L|0|1| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7~Bit6 => 00, Normal data packet
=> 01, Absolute coordination packet
=> 10, Notify packet
Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up.
When both fingers are up, the last two reports have zero valid
bit.
Bit4 => arc
Bit3 => 1
Bit2 => Left Button, 1 is pressed, 0 is released.
Bit1 => 0
Bit0 => 1
Byte 2: X coordinate (xpos[9:2])
Byte 3: Y coordinate (ypos[9:2])
Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0])
Bit3~Bit2 => X coordinate (ypos[1:0])
Bit5~Bit4 => y1_g
Bit7~Bit6 => x1_g
Packet 2 (ABSOLUTE POSITION)
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |0|1|V|A|1|R|1|0| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7~Bit6 => 00, Normal data packet
=> 01, Absolute coordinates packet
=> 10, Notify packet
Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up.
When both fingers are up, the last two reports have zero valid
bit.
Bit4 => arc
Bit3 => 1
Bit2 => Right Button, 1 is pressed, 0 is released.
Bit1 => 1
Bit0 => 0
Byte 2: X coordinate (xpos[9:2])
Byte 3: Y coordinate (ypos[9:2])
Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0])
Bit3~Bit2 => X coordinate (ypos[1:0])
Bit5~Bit4 => y2_g
Bit7~Bit6 => x2_g
Notify Packet for STL3888-A0
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |1|0|1|P|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |0|0|F|F|0|0|0|i| 4 |r|l|d|u|0|0|0|0|
|---------------| |---------------| |---------------| |---------------|
Byte 1: Bit7~Bit6 => 00, Normal data packet
=> 01, Absolute coordination packet
=> 10, Notify packet
Bit5 => 1
Bit4 => when in absolute coordinates mode (valid when EN_PKT_GO is 1):
0: left button is generated by the on-pad command
1: left button is generated by the external button
Bit3 => 1
Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
Bit1 => Right Button, 1 is pressed, 0 is not pressed.
Bit0 => Left Button, 1 is pressed, 0 is not pressed.
Byte 2: Message Type => 0xB7 (Multi Finger, Multi Coordinate mode)
Byte 3: Bit7~Bit6 => Don't care
Bit5~Bit4 => Number of fingers
Bit3~Bit1 => Reserved
Bit0 => 1: enter gesture mode; 0: leaving gesture mode
Byte 4: Bit7 => scroll right button
Bit6 => scroll left button
Bit5 => scroll down button
Bit4 => scroll up button
* Note that if gesture and additional button (Bit4~Bit7)
happen at the same time, the button information will not
be sent.
Bit3~Bit0 => Reserved
Sample sequence of Multi-finger, Multi-coordinate mode:
notify packet (valid bit == 1), abs pkt 1, abs pkt 2, abs pkt 1,
abs pkt 2, ..., notify packet(valid bit == 0)
==============================================================================
* FSP Enable/Disable packet
==============================================================================
Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
1 |Y|X|0|0|1|M|R|L| 2 |0|1|0|1|1|0|1|E| 3 | | | | | | | | | 4 | | | | | | | | |
|---------------| |---------------| |---------------| |---------------|
FSP will send out enable/disable packet when FSP receive PS/2 enable/disable
command. Host will receive the packet which Middle, Right, Left button will
be set. The packet only use byte 0 and byte 1 as a pattern of original packet.
Ignore the other bytes of the packet.
Byte 1: Bit7 => 0, Y overflow
Bit6 => 0, X overflow
Bit5 => 0, Y sign bit
Bit4 => 0, X sign bit
Bit3 => 1
Bit2 => 1, Middle Button
Bit1 => 1, Right Button
Bit0 => 1, Left Button
Byte 2: Bit7~1 => (0101101b)
Bit0 => 1 = Enable
0 = Disable
Byte 3: Don't care
Byte 4: Don't care (MOUSE ID 3, 4)
Byte 5~8: Don't care (Absolute packet)
==============================================================================
* PS/2 Command Set
==============================================================================
FSP supports basic PS/2 commanding set and modes, refer to following URL for
details about PS/2 commands:
http://www.computer-engineering.org/index.php?title=PS/2_Mouse_Interface
==============================================================================
* Programming Sequence for Determining Packet Parsing Flow
==============================================================================
1. Identify FSP by reading device ID(0x00) and version(0x01) register
2. Determine number of buttons by reading status2 (0x0b) register
buttons = reg[0x0b] & 0x30
if buttons == 0x30 or buttons == 0x20:
# two/four buttons
Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse'
section A for packet parsing detail(ignore byte 4, bit ~ 7)
elif buttons == 0x10:
# 6 buttons
Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse'
section B for packet parsing detail
elif buttons == 0x00:
# 6 buttons
Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse'
section A for packet parsing detail
==============================================================================
* Programming Sequence for Register Reading/Writing
==============================================================================
Register inversion requirement:
Following values needed to be inverted(the '~' operator in C) before being
sent to FSP:
0xe9, 0xee, 0xf2 and 0xff.
Register swapping requirement:
Following values needed to have their higher 4 bits and lower 4 bits being
swapped before being sent to FSP:
10, 20, 40, 60, 80, 100 and 200.
Register reading sequence:
1. send 0xf3 PS/2 command to FSP;
2. send 0x66 PS/2 command to FSP;
3. send 0x88 PS/2 command to FSP;
4. send 0xf3 PS/2 command to FSP;
5. if the register address being to read is not required to be
inverted(refer to the 'Register inversion requirement' section),
goto step 6
5a. send 0x68 PS/2 command to FSP;
5b. send the inverted register address to FSP and goto step 8;
6. if the register address being to read is not required to be
swapped(refer to the 'Register swapping requirement' section),
goto step 7
6a. send 0xcc PS/2 command to FSP;
6b. send the swapped register address to FSP and goto step 8;
7. send 0x66 PS/2 command to FSP;
7a. send the original register address to FSP and goto step 8;
8. send 0xe9(status request) PS/2 command to FSP;
9. the response read from FSP should be the requested register value.
Register writing sequence:
1. send 0xf3 PS/2 command to FSP;
2. if the register address being to write is not required to be
inverted(refer to the 'Register inversion requirement' section),
goto step 3
2a. send 0x74 PS/2 command to FSP;
2b. send the inverted register address to FSP and goto step 5;
3. if the register address being to write is not required to be
swapped(refer to the 'Register swapping requirement' section),
goto step 4
3a. send 0x77 PS/2 command to FSP;
3b. send the swapped register address to FSP and goto step 5;
4. send 0x55 PS/2 command to FSP;
4a. send the register address to FSP and goto step 5;
5. send 0xf3 PS/2 command to FSP;
6. if the register value being to write is not required to be
inverted(refer to the 'Register inversion requirement' section),
goto step 7
6a. send 0x47 PS/2 command to FSP;
6b. send the inverted register value to FSP and goto step 9;
7. if the register value being to write is not required to be
swapped(refer to the 'Register swapping requirement' section),
goto step 8
7a. send 0x44 PS/2 command to FSP;
7b. send the swapped register value to FSP and goto step 9;
8. send 0x33 PS/2 command to FSP;
8a. send the register value to FSP;
9. the register writing sequence is completed.
==============================================================================
* Register Listing
==============================================================================
offset width default r/w name
0x00 bit7~bit0 0x01 RO device ID
0x01 bit7~bit0 0xc0 RW version ID
0x02 bit7~bit0 0x01 RO vendor ID
0x03 bit7~bit0 0x01 RO product ID
0x04 bit3~bit0 0x01 RW revision ID
0x0b RO test mode status 1
bit3 1 RO 0: rotate 180 degree, 1: no rotation
bit5~bit4 RO number of buttons
11 => 2, lbtn/rbtn
10 => 4, lbtn/rbtn/scru/scrd
01 => 6, lbtn/rbtn/scru/scrd/scrl/scrr
00 => 6, lbtn/rbtn/scru/scrd/fbtn/bbtn
0x0f RW register file page control
bit0 0 RW 1 to enable page 1 register files
0x10 RW system control 1
bit0 1 RW Reserved, must be 1
bit1 0 RW Reserved, must be 0
bit4 1 RW Reserved, must be 0
bit5 0 RW register clock gating enable
0: read only, 1: read/write enable
(Note that following registers does not require clock gating being
enabled prior to write: 05 06 07 08 09 0c 0f 10 11 12 16 17 18 23 2e
40 41 42 43.)
0x31 RW on-pad command detection
bit7 0 RW on-pad command left button down tag
enable
0: disable, 1: enable
0x34 RW on-pad command control 5
bit4~bit0 0x05 RW XLO in 0s/4/1, so 03h = 0010.1b = 2.5
(Note that position unit is in 0.5 scanline)
bit7 0 RW on-pad tap zone enable
0: disable, 1: enable
0x35 RW on-pad command control 6
bit4~bit0 0x1d RW XHI in 0s/4/1, so 19h = 1100.1b = 12.5
(Note that position unit is in 0.5 scanline)
0x36 RW on-pad command control 7
bit4~bit0 0x04 RW YLO in 0s/4/1, so 03h = 0010.1b = 2.5
(Note that position unit is in 0.5 scanline)
0x37 RW on-pad command control 8
bit4~bit0 0x13 RW YHI in 0s/4/1, so 11h = 1000.1b = 8.5
(Note that position unit is in 0.5 scanline)
0x40 RW system control 5
bit1 0 RW FSP Intellimouse mode enable
0: disable, 1: enable
bit2 0 RW movement + abs. coordinate mode enable
0: disable, 1: enable
(Note that this function has the functionality of bit 1 even when
bit 1 is not set. However, the format is different from that of bit 1.
In addition, when bit 1 and bit 2 are set at the same time, bit 2 will
override bit 1.)
bit3 0 RW abs. coordinate only mode enable
0: disable, 1: enable
(Note that this function has the functionality of bit 1 even when
bit 1 is not set. However, the format is different from that of bit 1.
In addition, when bit 1, bit 2 and bit 3 are set at the same time,
bit 3 will override bit 1 and 2.)
bit5 0 RW auto switch enable
0: disable, 1: enable
bit6 0 RW G0 abs. + notify packet format enable
0: disable, 1: enable
(Note that the absolute/relative coordinate output still depends on
bit 2 and 3. That is, if any of those bit is 1, host will receive
absolute coordinates; otherwise, host only receives packets with
relative coordinate.)
0x43 RW on-pad control
bit0 0 RW on-pad control enable
0: disable, 1: enable
(Note that if this bit is cleared, bit 3/5 will be ineffective)
bit3 0 RW on-pad fix vertical scrolling enable
0: disable, 1: enable
bit5 0 RW on-pad fix horizontal scrolling enable
0: disable, 1: enable

View file

@ -107,6 +107,14 @@ config MOUSE_PS2_ELANTECH
entries. For further information, entries. For further information,
see <file:Documentation/input/elantech.txt>. see <file:Documentation/input/elantech.txt>.
config MOUSE_PS2_SENTELIC
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
depends on MOUSE_PS2
help
Say Y here if you have a laptop (such as MSI WIND Netbook)
with Sentelic Finger Sensing Pad touchpad.
If unsure, say N.
config MOUSE_PS2_TOUCHKIT config MOUSE_PS2_TOUCHKIT
bool "eGalax TouchKit PS/2 protocol extension" bool "eGalax TouchKit PS/2 protocol extension"

View file

@ -27,5 +27,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o

View file

@ -30,6 +30,7 @@
#include "trackpoint.h" #include "trackpoint.h"
#include "touchkit_ps2.h" #include "touchkit_ps2.h"
#include "elantech.h" #include "elantech.h"
#include "sentelic.h"
#define DRIVER_DESC "PS/2 mouse driver" #define DRIVER_DESC "PS/2 mouse driver"
@ -666,6 +667,20 @@ static int psmouse_extensions(struct psmouse *psmouse,
max_proto = PSMOUSE_IMEX; max_proto = PSMOUSE_IMEX;
} }
/*
* Try Finger Sensing Pad
*/
if (max_proto > PSMOUSE_IMEX) {
if (fsp_detect(psmouse, set_properties) == 0) {
if (!set_properties || fsp_init(psmouse) == 0)
return PSMOUSE_FSP;
/*
* Init failed, try basic relative protocols
*/
max_proto = PSMOUSE_IMEX;
}
}
if (max_proto > PSMOUSE_IMEX) { if (max_proto > PSMOUSE_IMEX) {
if (genius_detect(psmouse, set_properties) == 0) if (genius_detect(psmouse, set_properties) == 0)
return PSMOUSE_GENPS; return PSMOUSE_GENPS;
@ -813,7 +828,16 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.detect = elantech_detect, .detect = elantech_detect,
.init = elantech_init, .init = elantech_init,
}, },
#endif #endif
#ifdef CONFIG_MOUSE_PS2_SENTELIC
{
.type = PSMOUSE_FSP,
.name = "FSPPS/2",
.alias = "fsp",
.detect = fsp_detect,
.init = fsp_init,
},
#endif
{ {
.type = PSMOUSE_CORTRON, .type = PSMOUSE_CORTRON,
.name = "CortronPS/2", .name = "CortronPS/2",

View file

@ -91,6 +91,7 @@ enum psmouse_type {
PSMOUSE_CORTRON, PSMOUSE_CORTRON,
PSMOUSE_HGPK, PSMOUSE_HGPK,
PSMOUSE_ELANTECH, PSMOUSE_ELANTECH,
PSMOUSE_FSP,
PSMOUSE_AUTO /* This one should always be last */ PSMOUSE_AUTO /* This one should always be last */
}; };

View file

@ -0,0 +1,867 @@
/*-
* Finger Sensing Pad PS/2 mouse driver.
*
* Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
* Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/input.h>
#include <linux/ctype.h>
#include <linux/libps2.h>
#include <linux/serio.h>
#include <linux/jiffies.h>
#include "psmouse.h"
#include "sentelic.h"
/*
* Timeout for FSP PS/2 command only (in milliseconds).
*/
#define FSP_CMD_TIMEOUT 200
#define FSP_CMD_TIMEOUT2 30
/** Driver version. */
static const char fsp_drv_ver[] = "1.0.0-K";
/*
* Make sure that the value being sent to FSP will not conflict with
* possible sample rate values.
*/
static unsigned char fsp_test_swap_cmd(unsigned char reg_val)
{
switch (reg_val) {
case 10: case 20: case 40: case 60: case 80: case 100: case 200:
/*
* The requested value being sent to FSP matched to possible
* sample rates, swap the given value such that the hardware
* wouldn't get confused.
*/
return (reg_val >> 4) | (reg_val << 4);
default:
return reg_val; /* swap isn't necessary */
}
}
/*
* Make sure that the value being sent to FSP will not conflict with certain
* commands.
*/
static unsigned char fsp_test_invert_cmd(unsigned char reg_val)
{
switch (reg_val) {
case 0xe9: case 0xee: case 0xf2: case 0xff:
/*
* The requested value being sent to FSP matched to certain
* commands, inverse the given value such that the hardware
* wouldn't get confused.
*/
return ~reg_val;
default:
return reg_val; /* inversion isn't necessary */
}
}
static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
unsigned char addr;
int rc = -1;
/*
* We need to shut off the device and switch it into command
* mode so we don't confuse our protocol handler. We don't need
* to do that for writes because sysfs set helper does this for
* us.
*/
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
mutex_lock(&ps2dev->cmd_mutex);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
/* should return 0xfe(request for resending) */
ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
/* should return 0xfc(failed) */
ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2);
} else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
/* swapping is required */
ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2);
/* expect 0xfe */
} else {
/* swapping isn't necessary */
ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
/* expect 0xfe */
}
/* should return 0xfc(failed) */
ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT);
if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0)
goto out;
*reg_val = param[2];
rc = 0;
out:
mutex_unlock(&ps2dev->cmd_mutex);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
reg_addr, *reg_val, rc);
return rc;
}
static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char v;
int rc = -1;
mutex_lock(&ps2dev->cmd_mutex);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
/* inversion is required */
ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2);
} else {
if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
/* swapping is required */
ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2);
} else {
/* swapping isn't necessary */
ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2);
}
}
/* write the register address in correct order */
ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
return -1;
if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
/* inversion is required */
ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
/* swapping is required */
ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
} else {
/* swapping isn't necessary */
ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
}
/* write the register value in correct order */
ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
rc = 0;
out:
mutex_unlock(&ps2dev->cmd_mutex);
dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
reg_addr, reg_val, rc);
return rc;
}
/* Enable register clock gating for writing certain registers */
static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable)
{
int v, nv;
if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1)
return -1;
if (enable)
nv = v | FSP_BIT_EN_REG_CLK;
else
nv = v & ~FSP_BIT_EN_REG_CLK;
/* only write if necessary */
if (nv != v)
if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1)
return -1;
return 0;
}
static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
int rc = -1;
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
mutex_lock(&ps2dev->cmd_mutex);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2);
ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
/* get the returned result */
if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
goto out;
*reg_val = param[2];
rc = 0;
out:
mutex_unlock(&ps2dev->cmd_mutex);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
*reg_val, rc);
return rc;
}
static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char v;
int rc = -1;
mutex_lock(&ps2dev->cmd_mutex);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2);
ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
return -1;
if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
/* swapping is required */
ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
} else {
/* swapping isn't necessary */
ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
}
ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
rc = 0;
out:
mutex_unlock(&ps2dev->cmd_mutex);
dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
reg_val, rc);
return rc;
}
static int fsp_get_version(struct psmouse *psmouse, int *version)
{
if (fsp_reg_read(psmouse, FSP_REG_VERSION, version))
return -EIO;
return 0;
}
static int fsp_get_revision(struct psmouse *psmouse, int *rev)
{
if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev))
return -EIO;
return 0;
}
static int fsp_get_buttons(struct psmouse *psmouse, int *btn)
{
static const int buttons[] = {
0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */
0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */
0x04, /* Left/Middle/Right & Scroll Up/Down */
0x02, /* Left/Middle/Right */
};
int val;
if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1)
return -EIO;
*btn = buttons[(val & 0x30) >> 4];
return 0;
}
/* Enable on-pad command tag output */
static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
{
int v, nv;
int res = 0;
if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n");
return -EIO;
}
if (enable)
nv = v | FSP_BIT_EN_OPC_TAG;
else
nv = v & ~FSP_BIT_EN_OPC_TAG;
/* only write if necessary */
if (nv != v) {
fsp_reg_write_enable(psmouse, true);
res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv);
fsp_reg_write_enable(psmouse, false);
}
if (res != 0) {
dev_err(&psmouse->ps2dev.serio->dev,
"Unable to enable OPC tag.\n");
res = -EIO;
}
return res;
}
static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable)
{
struct fsp_data *pad = psmouse->private;
int val;
if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
return -EIO;
pad->vscroll = enable;
if (enable)
val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE);
else
val &= ~FSP_BIT_FIX_VSCR;
if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
return -EIO;
return 0;
}
static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)
{
struct fsp_data *pad = psmouse->private;
int val, v2;
if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
return -EIO;
if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2))
return -EIO;
pad->hscroll = enable;
if (enable) {
val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE);
v2 |= FSP_BIT_EN_MSID6;
} else {
val &= ~FSP_BIT_FIX_HSCR;
v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8);
}
if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
return -EIO;
/* reconfigure horizontal scrolling packet output */
if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2))
return -EIO;
return 0;
}
/*
* Write device specific initial parameters.
*
* ex: 0xab 0xcd - write oxcd into register 0xab
*/
static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
unsigned long reg, val;
char *rest;
ssize_t retval;
reg = simple_strtoul(buf, &rest, 16);
if (rest == buf || *rest != ' ' || reg > 0xff)
return -EINVAL;
if (strict_strtoul(rest + 1, 16, &val) || val > 0xff)
return -EINVAL;
if (fsp_reg_write_enable(psmouse, true))
return -EIO;
retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count;
fsp_reg_write_enable(psmouse, false);
return count;
}
PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg);
static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse,
void *data, char *buf)
{
struct fsp_data *pad = psmouse->private;
return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val);
}
/*
* Read a register from device.
*
* ex: 0xab -- read content from register 0xab
*/
static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
struct fsp_data *pad = psmouse->private;
unsigned long reg;
int val;
if (strict_strtoul(buf, 16, &reg) || reg > 0xff)
return -EINVAL;
if (fsp_reg_read(psmouse, reg, &val))
return -EIO;
pad->last_reg = reg;
pad->last_val = val;
return count;
}
PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL,
fsp_attr_show_getreg, fsp_attr_set_getreg);
static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,
void *data, char *buf)
{
int val = 0;
if (fsp_page_reg_read(psmouse, &val))
return -EIO;
return sprintf(buf, "%02x\n", val);
}
static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
unsigned long val;
if (strict_strtoul(buf, 16, &val) || val > 0xff)
return -EINVAL;
if (fsp_page_reg_write(psmouse, val))
return -EIO;
return count;
}
PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL,
fsp_attr_show_pagereg, fsp_attr_set_pagereg);
static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse,
void *data, char *buf)
{
struct fsp_data *pad = psmouse->private;
return sprintf(buf, "%d\n", pad->vscroll);
}
static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
unsigned long val;
if (strict_strtoul(buf, 10, &val) || val > 1)
return -EINVAL;
fsp_onpad_vscr(psmouse, val);
return count;
}
PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL,
fsp_attr_show_vscroll, fsp_attr_set_vscroll);
static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse,
void *data, char *buf)
{
struct fsp_data *pad = psmouse->private;
return sprintf(buf, "%d\n", pad->hscroll);
}
static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
unsigned long val;
if (strict_strtoul(buf, 10, &val) || val > 1)
return -EINVAL;
fsp_onpad_hscr(psmouse, val);
return count;
}
PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL,
fsp_attr_show_hscroll, fsp_attr_set_hscroll);
static ssize_t fsp_attr_show_flags(struct psmouse *psmouse,
void *data, char *buf)
{
struct fsp_data *pad = psmouse->private;
return sprintf(buf, "%c\n",
pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c');
}
static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data,
const char *buf, size_t count)
{
struct fsp_data *pad = psmouse->private;
size_t i;
for (i = 0; i < count; i++) {
switch (buf[i]) {
case 'C':
pad->flags |= FSPDRV_FLAG_EN_OPC;
break;
case 'c':
pad->flags &= ~FSPDRV_FLAG_EN_OPC;
break;
default:
return -EINVAL;
}
}
return count;
}
PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL,
fsp_attr_show_flags, fsp_attr_set_flags);
static ssize_t fsp_attr_show_ver(struct psmouse *psmouse,
void *data, char *buf)
{
return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver);
}
PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver);
static struct attribute *fsp_attributes[] = {
&psmouse_attr_setreg.dattr.attr,
&psmouse_attr_getreg.dattr.attr,
&psmouse_attr_page.dattr.attr,
&psmouse_attr_vscroll.dattr.attr,
&psmouse_attr_hscroll.dattr.attr,
&psmouse_attr_flags.dattr.attr,
&psmouse_attr_ver.dattr.attr,
NULL
};
static struct attribute_group fsp_attribute_group = {
.attrs = fsp_attributes,
};
#ifdef FSP_DEBUG
static void fsp_packet_debug(unsigned char packet[])
{
static unsigned int ps2_packet_cnt;
static unsigned int ps2_last_second;
unsigned int jiffies_msec;
ps2_packet_cnt++;
jiffies_msec = jiffies_to_msecs(jiffies);
printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
if (jiffies_msec - ps2_last_second > 1000) {
printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt);
ps2_packet_cnt = 0;
ps2_last_second = jiffies_msec;
}
}
#else
static void fsp_packet_debug(unsigned char packet[])
{
}
#endif
static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct fsp_data *ad = psmouse->private;
unsigned char *packet = psmouse->packet;
unsigned char button_status = 0, lscroll = 0, rscroll = 0;
int rel_x, rel_y;
if (psmouse->pktcnt < 4)
return PSMOUSE_GOOD_DATA;
/*
* Full packet accumulated, process it
*/
switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
case FSP_PKT_TYPE_ABS:
dev_warn(&psmouse->ps2dev.serio->dev,
"Unexpected absolute mode packet, ignored.\n");
break;
case FSP_PKT_TYPE_NORMAL_OPC:
/* on-pad click, filter it if necessary */
if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
packet[0] &= ~BIT(0);
/* fall through */
case FSP_PKT_TYPE_NORMAL:
/* normal packet */
/* special packet data translation from on-pad packets */
if (packet[3] != 0) {
if (packet[3] & BIT(0))
button_status |= 0x01; /* wheel down */
if (packet[3] & BIT(1))
button_status |= 0x0f; /* wheel up */
if (packet[3] & BIT(2))
button_status |= BIT(5);/* horizontal left */
if (packet[3] & BIT(3))
button_status |= BIT(4);/* horizontal right */
/* push back to packet queue */
if (button_status != 0)
packet[3] = button_status;
rscroll = (packet[3] >> 4) & 1;
lscroll = (packet[3] >> 5) & 1;
}
/*
* Processing wheel up/down and extra button events
*/
input_report_rel(dev, REL_WHEEL,
(int)(packet[3] & 8) - (int)(packet[3] & 7));
input_report_rel(dev, REL_HWHEEL, lscroll - rscroll);
input_report_key(dev, BTN_BACK, lscroll);
input_report_key(dev, BTN_FORWARD, rscroll);
/*
* Standard PS/2 Mouse
*/
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);
rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
input_report_rel(dev, REL_X, rel_x);
input_report_rel(dev, REL_Y, rel_y);
break;
}
input_sync(dev);
fsp_packet_debug(packet);
return PSMOUSE_FULL_PACKET;
}
static int fsp_activate_protocol(struct psmouse *psmouse)
{
struct fsp_data *pad = psmouse->private;
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
int val;
/*
* Standard procedure to enter FSP Intellimouse mode
* (scrolling wheel, 4th and 5th buttons)
*/
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 80;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 0x04) {
dev_err(&psmouse->ps2dev.serio->dev,
"Unable to enable 4 bytes packet format.\n");
return -EIO;
}
if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
dev_err(&psmouse->ps2dev.serio->dev,
"Unable to read SYSCTL5 register.\n");
return -EIO;
}
val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
/* Ensure we are not in absolute mode */
val &= ~FSP_BIT_EN_PKT_G0;
if (pad->buttons == 0x06) {
/* Left/Middle/Right & Scroll Up/Down/Right/Left */
val |= FSP_BIT_EN_MSID6;
}
if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
dev_err(&psmouse->ps2dev.serio->dev,
"Unable to set up required mode bits.\n");
return -EIO;
}
/*
* Enable OPC tags such that driver can tell the difference between
* on-pad and real button click
*/
if (fsp_opc_tag_enable(psmouse, true))
dev_warn(&psmouse->ps2dev.serio->dev,
"Failed to enable OPC tag mode.\n");
/* Enable on-pad vertical and horizontal scrolling */
fsp_onpad_vscr(psmouse, true);
fsp_onpad_hscr(psmouse, true);
return 0;
}
int fsp_detect(struct psmouse *psmouse, int set_properties)
{
int id;
if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id))
return -EIO;
if (id != 0x01)
return -ENODEV;
if (set_properties) {
psmouse->vendor = "Sentelic";
psmouse->name = "FingerSensingPad";
}
return 0;
}
static void fsp_reset(struct psmouse *psmouse)
{
fsp_opc_tag_enable(psmouse, false);
fsp_onpad_vscr(psmouse, false);
fsp_onpad_hscr(psmouse, false);
}
static void fsp_disconnect(struct psmouse *psmouse)
{
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
&fsp_attribute_group);
fsp_reset(psmouse);
kfree(psmouse->private);
}
static int fsp_reconnect(struct psmouse *psmouse)
{
int version;
if (fsp_detect(psmouse, 0))
return -ENODEV;
if (fsp_get_version(psmouse, &version))
return -ENODEV;
if (fsp_activate_protocol(psmouse))
return -EIO;
return 0;
}
int fsp_init(struct psmouse *psmouse)
{
struct fsp_data *priv;
int ver, rev, buttons;
int error;
if (fsp_get_version(psmouse, &ver) ||
fsp_get_revision(psmouse, &rev) ||
fsp_get_buttons(psmouse, &buttons)) {
return -ENODEV;
}
printk(KERN_INFO
"Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->ver = ver;
priv->rev = rev;
priv->buttons = buttons;
/* enable on-pad click by default */
priv->flags |= FSPDRV_FLAG_EN_OPC;
/* Set up various supported input event bits */
__set_bit(BTN_BACK, psmouse->dev->keybit);
__set_bit(BTN_FORWARD, psmouse->dev->keybit);
__set_bit(REL_WHEEL, psmouse->dev->relbit);
__set_bit(REL_HWHEEL, psmouse->dev->relbit);
psmouse->protocol_handler = fsp_process_byte;
psmouse->disconnect = fsp_disconnect;
psmouse->reconnect = fsp_reconnect;
psmouse->cleanup = fsp_reset;
psmouse->pktsize = 4;
/* set default packet output based on number of buttons we found */
error = fsp_activate_protocol(psmouse);
if (error)
goto err_out;
error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
&fsp_attribute_group);
if (error) {
dev_err(&psmouse->ps2dev.serio->dev,
"Failed to create sysfs attributes (%d)", error);
goto err_out;
}
return 0;
err_out:
kfree(psmouse->private);
psmouse->private = NULL;
return error;
}

View file

@ -0,0 +1,98 @@
/*-
* Finger Sensing Pad PS/2 mouse driver.
*
* Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
* Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __SENTELIC_H
#define __SENTELIC_H
/* Finger-sensing Pad information registers */
#define FSP_REG_DEVICE_ID 0x00
#define FSP_REG_VERSION 0x01
#define FSP_REG_REVISION 0x04
#define FSP_REG_TMOD_STATUS1 0x0B
#define FSP_BIT_NO_ROTATION BIT(3)
#define FSP_REG_PAGE_CTRL 0x0F
/* Finger-sensing Pad control registers */
#define FSP_REG_SYSCTL1 0x10
#define FSP_BIT_EN_REG_CLK BIT(5)
#define FSP_REG_OPC_QDOWN 0x31
#define FSP_BIT_EN_OPC_TAG BIT(7)
#define FSP_REG_OPTZ_XLO 0x34
#define FSP_REG_OPTZ_XHI 0x35
#define FSP_REG_OPTZ_YLO 0x36
#define FSP_REG_OPTZ_YHI 0x37
#define FSP_REG_SYSCTL5 0x40
#define FSP_BIT_90_DEGREE BIT(0)
#define FSP_BIT_EN_MSID6 BIT(1)
#define FSP_BIT_EN_MSID7 BIT(2)
#define FSP_BIT_EN_MSID8 BIT(3)
#define FSP_BIT_EN_AUTO_MSID8 BIT(5)
#define FSP_BIT_EN_PKT_G0 BIT(6)
#define FSP_REG_ONPAD_CTL 0x43
#define FSP_BIT_ONPAD_ENABLE BIT(0)
#define FSP_BIT_ONPAD_FBBB BIT(1)
#define FSP_BIT_FIX_VSCR BIT(3)
#define FSP_BIT_FIX_HSCR BIT(5)
#define FSP_BIT_DRAG_LOCK BIT(6)
/* Finger-sensing Pad packet formating related definitions */
/* absolute packet type */
#define FSP_PKT_TYPE_NORMAL (0x00)
#define FSP_PKT_TYPE_ABS (0x01)
#define FSP_PKT_TYPE_NOTIFY (0x02)
#define FSP_PKT_TYPE_NORMAL_OPC (0x03)
#define FSP_PKT_TYPE_SHIFT (6)
#ifdef __KERNEL__
struct fsp_data {
unsigned char ver; /* hardware version */
unsigned char rev; /* hardware revison */
unsigned char buttons; /* Number of buttons */
unsigned int flags;
#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */
bool vscroll; /* Vertical scroll zone enabled */
bool hscroll; /* Horizontal scroll zone enabled */
unsigned char last_reg; /* Last register we requested read from */
unsigned char last_val;
};
#ifdef CONFIG_MOUSE_PS2_SENTELIC
extern int fsp_detect(struct psmouse *psmouse, int set_properties);
extern int fsp_init(struct psmouse *psmouse);
#else
inline int fsp_detect(struct psmouse *psmouse, int set_properties)
{
return -ENOSYS;
}
inline int fsp_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
#endif
#endif /* __KERNEL__ */
#endif /* !__SENTELIC_H */

View file

@ -161,7 +161,7 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
* ps2_command() can only be called from a process context * ps2_command() can only be called from a process context
*/ */
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{ {
int timeout; int timeout;
int send = (command >> 12) & 0xf; int send = (command >> 12) & 0xf;
@ -179,8 +179,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
return -1; return -1;
} }
mutex_lock(&ps2dev->cmd_mutex);
serio_pause_rx(ps2dev->serio); serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive; ps2dev->cmdcnt = receive;
@ -231,7 +229,18 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0; ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio); serio_continue_rx(ps2dev->serio);
return rc;
}
EXPORT_SYMBOL(__ps2_command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int rc;
mutex_lock(&ps2dev->cmd_mutex);
rc = __ps2_command(ps2dev, param, command);
mutex_unlock(&ps2dev->cmd_mutex); mutex_unlock(&ps2dev->cmd_mutex);
return rc; return rc;
} }
EXPORT_SYMBOL(ps2_command); EXPORT_SYMBOL(ps2_command);

View file

@ -44,6 +44,7 @@ struct ps2dev {
void ps2_init(struct ps2dev *ps2dev, struct serio *serio); void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout);
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout);
int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);