From 49efa33dd091a76d6475d1216dc8c56f606ebb6b Mon Sep 17 00:00:00 2001 From: PulkoMandy Date: Tue, 17 Jan 2023 20:37:32 +0100 Subject: [PATCH] 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 Reviewed-by: Adrien Destugues Reviewed-by: nephele --- headers/private/input/keyboard_mouse_driver.h | 4 + .../kernel/bus_managers/ps2/ps2_synaptics.cpp | 223 +++++++++++++++--- .../kernel/bus_managers/ps2/ps2_synaptics.h | 43 ++-- 3 files changed, 206 insertions(+), 64 deletions(-) diff --git a/headers/private/input/keyboard_mouse_driver.h b/headers/private/input/keyboard_mouse_driver.h index 248ea3293a..c2626c5c6d 100644 --- a/headers/private/input/keyboard_mouse_driver.h +++ b/headers/private/input/keyboard_mouse_driver.h @@ -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 diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp index 61fdc5bb09..b0c39327f7 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp @@ -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]; diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h index fd12cbf2ce..0f08cf4b28 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h @@ -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;