ps2 synaptics: implement 'extended W' mode.

"Newer" synaptics touchpad support a new mode where they can report more
information to the host. In this mode, there is a different packet
format for tracking extra data from the touchpad, including a wheel
encoder (mousewheel) if available, and multitouch finger tracking.

This mode is documented in the Synaptics touchpad interfacing guide
(Synaptics document 511-000275-01 Rev. B), but was not yet implemented
in our driver.

It should help with detecting multiple fingers, or finger position on
clickpads to determine right or left click.

This change implements the following items from the Synaptics
interfacing guide:

- Cleanup and clarify the code for features detection to properly report
  clickpads
- Enable "extended W" mode if supported
- Process extended W values 0 (mouse wheels, reported in the
  touchpad_event structure and could be used by input_server for
  scrolling), 1 (secondary finger), and 2 (finger count)
- Fix handling of wValue, which is not always a finger width
- Add handling of vValuen which indicates the finger width when wValue
  doesn't

Overall, this should provide the movement_maker with a better picture of
what's happening.

Also improve tracing to show received packets and the corresponding
WValue since that's an important value in identifying which type of
packet it is.

Unfortunately I currently don't have a laptop with synaptics touchpad to
test this with.

Change-Id: If334392f4eb2a146955f6c8c897f0ab64d79b8d9
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4425
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
Reviewed-by: nephele <nep@packageloss.eu>
This commit is contained in:
PulkoMandy 2023-01-17 20:37:32 +01:00 committed by Adrien Destugues
parent 64a48a5521
commit 49efa33dd0
3 changed files with 206 additions and 64 deletions

View File

@ -152,6 +152,10 @@ typedef struct {
uint8 fingers;
bool gesture;
uint8 fingerWidth;
int32 wheel_ydelta;
int32 wheel_xdelta;
int32 wheel_zdelta;
int32 wheel_wdelta;
// 1 - 4 normal width
// 5 - 11 very wide finger or palm
// 12 maximum reportable width; extreme wide contact

View File

@ -64,6 +64,29 @@ enum {
static touchpad_specs gHardwareSpecs;
typedef struct {
uint8 majorVersion;
uint8 minorVersion;
uint8 nExtendedButtons;
uint8 firstExtendedButton;
uint8 extendedButtonsState;
bool capExtended : 1;
bool capMiddleButton : 1;
bool capSleep : 1;
bool capFourButtons : 1;
bool capMultiFinger : 1;
bool capMultiFingerReport : 1;
bool capPalmDetection : 1;
bool capPassThrough : 1;
bool capAdvancedGestures : 1;
bool capExtendedWMode : 1;
bool capClickPadUniform : 1;
int capClickPadButtonCount : 2;
} touchpad_info;
const char* kSynapticsPath[4] = {
"input/touchpad/ps2/synaptics_0",
"input/touchpad/ps2/synaptics_1",
@ -131,7 +154,7 @@ static status_t
get_synaptics_movment(synaptics_cookie *cookie, touchpad_movement *_event, bigtime_t timeout)
{
status_t status;
touchpad_movement event;
touchpad_movement event = {};
uint8 event_buffer[PS2_MAX_PACKET_SIZE];
uint8 wValue0, wValue1, wValue2, wValue3, wValue;
uint32 val32;
@ -155,6 +178,7 @@ get_synaptics_movment(synaptics_cookie *cookie, touchpad_movement *_event, bigti
event.buttons = event_buffer[0] & 3;
event.zPressure = event_buffer[2];
bool isExtended = false;
if (sTouchpadInfo.capExtended) {
wValue0 = event_buffer[3] >> 2 & 1;
@ -167,12 +191,98 @@ get_synaptics_movment(synaptics_cookie *cookie, touchpad_movement *_event, bigti
wValue = wValue | (wValue2 << 2);
wValue = wValue | (wValue3 << 3);
event.fingerWidth = wValue;
TRACE("SYNAPTICS: received packet %02x %02x %02x %02x %02x %02x -> W = %d\n",
event_buffer[0], event_buffer[1], event_buffer[2], event_buffer[3], event_buffer[4],
event_buffer[5], wValue);
if (wValue <= 1) {
// Multiple fingers detected, report the finger count.
event.fingers = 2 + wValue;
// There is a separate "v" value indicating the finger width
wValue0 = event_buffer[3] >> 1 & 1;
wValue1 = event_buffer[4] >> 1 & 1;
wValue2 = event_buffer[2] >> 0 & 1;
wValue = wValue0;
wValue = wValue | (wValue1 << 1);
wValue = wValue | (wValue2 << 2);
event.fingerWidth = wValue * 2;
} else if (wValue >= 4) {
// wValue = 4 to 15 corresponds to a finger width report for a single finger
event.fingerWidth = wValue;
event.fingers = 1;
}
// wValue = 2 - Extended W mode
// wValue = 3 - Packet from pass-through device
event.gesture = false;
if (sTouchpadInfo.capExtendedWMode && wValue == 2) {
// The extended W can be encoded on 4 bits (values 0-7) or 8 bits (80-FF)
uint8_t extendedWValue = event_buffer[5] >> 4;
if (extendedWValue >= 8)
extendedWValue = event_buffer[5];
isExtended = true;
// Extended W = 0 - Scroll wheels
if (extendedWValue == 0) {
event.wheel_ydelta = event_buffer[2];
event.wheel_xdelta = event_buffer[3];
event.wheel_zdelta = event_buffer[4];
event.wheel_wdelta = (event_buffer[5] & 0x0f) | (event_buffer[3] & 0x30);
// Sign extend wdelta if needed
if (event.wheel_wdelta & 0x20)
event.wheel_wdelta |= 0xffffffc0;
}
// Extended W = 1 - Secondary finger
if (extendedWValue == 1) {
event.xPosition = event_buffer[1];
event.yPosition = event_buffer[2];
val32 = event_buffer[4] & 0x0F;
event.xPosition += val32 << 8;
val32 = event_buffer[4] & 0xF0;
event.yPosition += val32 << 4;
event.xPosition *= 2;
event.yPosition *= 2;
event.zPressure = event_buffer[5] & 0x0F;
event.zPressure += event_buffer[3] & 0x30;
// There is a separate "v" value indicating the finger width
wValue0 = event_buffer[1] >> 0 & 1;
wValue1 = event_buffer[2] >> 0 & 1;
wValue2 = event_buffer[5] >> 0 & 1;
wValue = wValue0;
wValue = wValue | (wValue1 << 1);
wValue = wValue | (wValue2 << 2);
event.fingerWidth = wValue * 2;
}
// Extended W = 2 - Finger state info
if (extendedWValue == 2) {
event.fingers = event_buffer[1] & 0x0F;
// TODO this event also provides primary and secondary finger indexes, what do
// these mean?
}
// Other values are reserved for other uses (usually enabled with special commands)
*_event = event;
return status;
}
// Clickpad pretends that all clicks on the touchpad are middle clicks.
// Pass them to userspace as left clicks instead.
if (sTouchpadInfo.capClickPad)
if (sTouchpadInfo.capClickPadButtonCount == 1)
event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01);
if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons)
@ -212,18 +322,20 @@ get_synaptics_movment(synaptics_cookie *cookie, touchpad_movement *_event, bigti
event.gesture = event_buffer[0] >> 2 & 1;
}
event.xPosition = event_buffer[4];
event.yPosition = event_buffer[5];
if (!isExtended) {
event.xPosition = event_buffer[4];
event.yPosition = event_buffer[5];
val32 = event_buffer[1] & 0x0F;
event.xPosition += val32 << 8;
val32 = event_buffer[1] >> 4 & 0x0F;
event.yPosition += val32 << 8;
val32 = event_buffer[1] & 0x0F;
event.xPosition += val32 << 8;
val32 = event_buffer[1] >> 4 & 0x0F;
event.yPosition += val32 << 8;
xTwelfBit = event_buffer[3] >> 4 & 1;
event.xPosition += xTwelfBit << 12;
yTwelfBit = event_buffer[3] >> 5 & 1;
event.yPosition += yTwelfBit << 12;
xTwelfBit = event_buffer[3] >> 4 & 1;
event.xPosition += xTwelfBit << 12;
yTwelfBit = event_buffer[3] >> 5 & 1;
event.yPosition += yTwelfBit << 12;
}
*_event = event;
return B_OK;
@ -245,35 +357,66 @@ query_capability(ps2_dev *dev)
TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1);
sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1;
TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
sTouchpadInfo.capSleep = val[2] >> 4 & 1;
TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
sTouchpadInfo.capPalmDetection = val[2] & 1;
TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
// bit 6, low power, is only informative (touchpad has an automatic powersave mode)
TRACE("SYNAPTICS: multi finger report %2x\n", val[2] >> 5 & 1);
sTouchpadInfo.capMultiFingerReport = val[2] >> 5 & 1;
TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
sTouchpadInfo.capSleep = val[2] >> 4 & 1;
TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
// bit 2, TouchStyk ballistics, not further documented in the documents I have
TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
sTouchpadInfo.capPalmDetection = val[2] & 1;
if (get_information_query(dev, nExtendedQueries, kContinuedCapabilities,
val) == B_OK) {
sTouchpadInfo.capAdvancedGestures = val[0] >> 3 & 1;
TRACE("SYNAPTICS: advanced gestures %x\n", sTouchpadInfo.capAdvancedGestures);
sTouchpadInfo.capClickPadButtonCount = (val[0] >> 4 & 1) | ((val[1] >> 0 & 1) << 1);
sTouchpadInfo.capClickPadUniform = val[1] >> 4 & 1;
TRACE("SYNAPTICS: clickpad buttons: %x\n", sTouchpadInfo.capClickPadButtonCount);
TRACE("SYNAPTICS: clickpad type: %s\n",
sTouchpadInfo.capClickPadUniform ? "uniform" : "hinged");
} else {
sTouchpadInfo.capAdvancedGestures = 0;
sTouchpadInfo.capClickPadButtonCount = 0;
sTouchpadInfo.capClickPadUniform = 0;
sTouchpadInfo.capAdvancedGestures = 0;
}
if (get_information_query(dev, nExtendedQueries, kExtendedModelId, val)
!= B_OK) {
// "Extended Model ID" is not supported, so there cannot be extra
// buttons.
sTouchpadInfo.nExtendedButtons = 0;
sTouchpadInfo.firstExtendedButton = 0;
sTouchpadInfo.capClickPad = false;
sTouchpadInfo.capExtendedWMode = 0;
return;
}
sTouchpadInfo.capClickPad = (val[0] >> 5 & 1) | (val[1] >> 0 & 1);
TRACE("SYNAPTICS: clickpad %x\n", sTouchpadInfo.capClickPad);
TRACE("SYNAPTICS: extended buttons %2x\n", val[1] >> 4 & 15);
sTouchpadInfo.nExtendedButtons = val[1] >> 4 & 15;
sTouchpadInfo.extendedButtonsState = 0;
if (sTouchpadInfo.capMiddleButton)
sTouchpadInfo.capExtendedWMode = val[0] >> 2 & 1;
TRACE("SYNAPTICS: extended wmode %2x\n", sTouchpadInfo.capExtendedWMode);
if (sTouchpadInfo.capFourButtons)
sTouchpadInfo.firstExtendedButton = 4;
else if (sTouchpadInfo.capMiddleButton)
sTouchpadInfo.firstExtendedButton = 3;
else
sTouchpadInfo.firstExtendedButton = 2;
@ -507,10 +650,12 @@ synaptics_open(const char *name, uint32 flags, void **_cookie)
}
// Set Mode
if (sTouchpadInfo.capExtended)
cookie->mode = SYN_ABSOLUTE_W_MODE;
if (sTouchpadInfo.capExtendedWMode)
cookie->mode = SYN_MODE_ABSOLUTE | SYN_MODE_W | SYN_MODE_EXTENDED_W;
else if (sTouchpadInfo.capExtended)
cookie->mode = SYN_MODE_ABSOLUTE | SYN_MODE_W;
else
cookie->mode = SYN_ABSOLUTE_MODE;
cookie->mode = SYN_MODE_ABSOLUTE;
status = set_touchpad_mode(dev, cookie->mode);
if (status < B_OK) {
@ -638,22 +783,21 @@ synaptics_handle_int(ps2_dev *dev)
val = cookie->dev->history[0].data;
if ((cookie->packet_index == 0 || cookie->packet_index == 3)
&& (val & 8) != 0) {
INFO("SYNAPTICS: bad mouse data, trying resync\n");
if ((cookie->packet_index == 0 || cookie->packet_index == 3) && (val & 8) != 0) {
INFO("SYNAPTICS: bad mouse data %#02x, trying resync\n", val);
cookie->packet_index = 0;
return B_UNHANDLED_INTERRUPT;
}
if (cookie->packet_index == 0 && val >> 6 != 0x02) {
TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
TRACE("SYNAPTICS: first package %#02x begins not with bit 1, 0\n", val);
return B_UNHANDLED_INTERRUPT;
}
if (cookie->packet_index == 3 && val >> 6 != 0x03) {
TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
cookie->packet_index = 0;
}
if (cookie->packet_index == 3 && val >> 6 != 0x03) {
TRACE("SYNAPTICS: third package %#02x begins not with bit 1, 1\n", val);
cookie->packet_index = 0;
return B_UNHANDLED_INTERRUPT;
}
cookie->buffer[cookie->packet_index] = val;
}
cookie->buffer[cookie->packet_index] = val;
cookie->packet_index++;
if (cookie->packet_index >= 6) {
@ -664,6 +808,7 @@ synaptics_handle_int(ps2_dev *dev)
if (sPassthroughDevice->active
&& sPassthroughDevice->handle_int != NULL
&& IS_SYN_PT_PACKAGE(cookie->buffer)) {
TRACE("SYNAPTICS: forward packet to passthrough device\n");
status_t status;
sPassthroughDevice->history[0].data = cookie->buffer[1];

View File

@ -16,13 +16,25 @@
#define SYN_TOUCHPAD 0x47
// Synaptics modes
#define SYN_ABSOLUTE_MODE 0x80
// Absolute plus w mode
#define SYN_ABSOLUTE_W_MODE 0x81
// Synaptics modes (values of the "mode" field in synaptics_cookie)
#define SYN_MODE_ABSOLUTE (1 << 7)
// Absolute mode reports the absolute X/Y position of the finger,
// instead of relative X/Y movement
#define SYN_MODE_W (1 << 0)
// Adds finger width (W) value in addition to absolute X/Y
#define SYN_MODE_EXTENDED_W (1 << 2)
// Supports tracking for multiple fingers
#define SYN_FOUR_BYTE_CHILD (1 << 1)
// Low power sleep mode
#define SYN_SLEEP_MODE 0x0C
// Guest packets size for pass-through device
#define SYN_MODE_SLEEP (1 << 3)
// Low power sleep mode
#define SYN_MODE_PASSTHROUGH_ACPI (1 << 4)
#define SYN_MODE_PASSTHROUGH_TRANSPARENT (1 << 5)
#define SYN_MODE_HIGH_RATE (1 << 6)
// Use 80 packets per second instead of 40
// Synaptics Passthrough port
#define SYN_CHANGE_MODE 0x14
#define SYN_PASSTHROUGH_CMD 0x28
@ -35,25 +47,6 @@
&& (val[3] & 0xCC) == 0xc4)
typedef struct {
uint8 majorVersion;
uint8 minorVersion;
bool capExtended;
bool capMiddleButton;
bool capSleep;
bool capFourButtons;
bool capMultiFinger;
bool capPalmDetection;
bool capPassThrough;
bool capClickPad;
uint8 nExtendedButtons;
uint8 firstExtendedButton;
uint8 extendedButtonsState;
} touchpad_info;
typedef struct {
ps2_dev* dev;