Input: add new driver for Sentelic Finger Sensing Pad
This is the driver for Sentelic Finger Sensing Pad which can be found on MSI WIND Netbook. Signed-off-by: Tai-hwa Liang <avatar@sentelic.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
Родитель
3b72094409
Коммит
fc69f4a6af
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -91,6 +91,7 @@ enum psmouse_type {
|
|||
PSMOUSE_CORTRON,
|
||||
PSMOUSE_HGPK,
|
||||
PSMOUSE_ELANTECH,
|
||||
PSMOUSE_FSP,
|
||||
PSMOUSE_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
|
|
|
@ -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 > 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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче