aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTai-hwa Liang <avatar@sentelic.com>2009-05-10 18:15:39 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-08-19 21:46:09 -0700
commitfc69f4a6af49ee69475dc4217924d9edf77760e0 (patch)
tree2b200846dc0848e8fa85f439d9a1c98652356348
parent3b72094409ab673d096b3852f4636be540780faf (diff)
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>
-rw-r--r--Documentation/input/sentelic.txt475
-rw-r--r--drivers/input/mouse/Kconfig8
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/psmouse-base.c26
-rw-r--r--drivers/input/mouse/psmouse.h1
-rw-r--r--drivers/input/mouse/sentelic.c867
-rw-r--r--drivers/input/mouse/sentelic.h98
-rw-r--r--drivers/input/serio/libps2.c15
-rw-r--r--include/linux/libps2.h1
9 files changed, 1488 insertions, 4 deletions
diff --git a/Documentation/input/sentelic.txt b/Documentation/input/sentelic.txt
new file mode 100644
index 00000000000..f7160a2fb6a
--- /dev/null
+++ b/Documentation/input/sentelic.txt
@@ -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
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 90bef5d498f..3feeb3af8ab 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -107,6 +107,14 @@ config MOUSE_PS2_ELANTECH
entries. For further information,
see <file:Documentation/input/elantech.txt>.
+config MOUSE_PS2_SENTELIC
+ bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a laptop (such as MSI WIND Netbook)
+ with Sentelic Finger Sensing Pad touchpad.
+
+ If unsure, say N.
config MOUSE_PS2_TOUCHKIT
bool "eGalax TouchKit PS/2 protocol extension"
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index ea58c9a372b..570c84a4a65 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -27,5 +27,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
+psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index b407b355dce..df318887ca0 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -30,6 +30,7 @@
#include "trackpoint.h"
#include "touchkit_ps2.h"
#include "elantech.h"
+#include "sentelic.h"
#define DRIVER_DESC "PS/2 mouse driver"
@@ -666,6 +667,20 @@ static int psmouse_extensions(struct psmouse *psmouse,
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 (genius_detect(psmouse, set_properties) == 0)
return PSMOUSE_GENPS;
@@ -813,7 +828,16 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.detect = elantech_detect,
.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,
.name = "CortronPS/2",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index e3562daeb2e..cca1744c2a0 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -91,6 +91,7 @@ enum psmouse_type {
PSMOUSE_CORTRON,
PSMOUSE_HGPK,
PSMOUSE_ELANTECH,
+ PSMOUSE_FSP,
PSMOUSE_AUTO /* This one should always be last */
};
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
new file mode 100644
index 00000000000..97b1e72855a
--- /dev/null
+++ b/drivers/input/mouse/sentelic.c
@@ -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;
+}
diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h
new file mode 100644
index 00000000000..083559c7282
--- /dev/null
+++ b/drivers/input/mouse/sentelic.h
@@ -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 */
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index be5bbbb8ae4..3a95b508bf2 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -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
*/
-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 send = (command >> 12) & 0xf;
@@ -179,8 +179,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
return -1;
}
- mutex_lock(&ps2dev->cmd_mutex);
-
serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
@@ -231,7 +229,18 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0;
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);
+
return rc;
}
EXPORT_SYMBOL(ps2_command);
diff --git a/include/linux/libps2.h b/include/linux/libps2.h
index b94534b7e26..fcf5fbe6a50 100644
--- a/include/linux/libps2.h
+++ b/include/linux/libps2.h
@@ -44,6 +44,7 @@ struct ps2dev {
void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, 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_handle_ack(struct ps2dev *ps2dev, unsigned char data);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);