Input: add new driver for Sentelic Finger Sensing Pad
Tai-hwa Liang [Mon, 11 May 2009 01:15:39 +0000 (18:15 -0700)]
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>

Documentation/input/sentelic.txt [new file with mode: 0644]
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/sentelic.c [new file with mode: 0644]
drivers/input/mouse/sentelic.h [new file with mode: 0644]
drivers/input/serio/libps2.c
include/linux/libps2.h

diff --git a/Documentation/input/sentelic.txt b/Documentation/input/sentelic.txt
new file mode 100644 (file)
index 0000000..f7160a2
--- /dev/null
@@ -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
index 90bef5d..3feeb3a 100644 (file)
@@ -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"
index ea58c9a..570c84a 100644 (file)
@@ -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
index b407b35..df31888 100644 (file)
@@ -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",
index e3562da..cca1744 100644 (file)
@@ -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 (file)
index 0000000..97b1e72
--- /dev/null
@@ -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 (file)
index 0000000..083559c
--- /dev/null
@@ -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 */
index be5bbbb..3a95b50 100644 (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
  */
 
-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);
index b94534b..fcf5fbe 100644 (file)
@@ -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);