From 494ae23c3d1b531864ded9ccf7d51a40933d1b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Rodr=C3=ADguez=20P=C3=A9rez?= Date: Mon, 17 Nov 2025 14:11:13 +0000 Subject: [PATCH] ps/2 elantech: trackpoint support (untested) Threat trackpoint packets to mouse_movement values instead of touchpad_movement. This required changes on ioctl handling effectely making this as kind of hybrid device which reports back MS_READ events for touchpad and MS_READ_TOUCHPAD for touchpad related processed packets. The logic is similar to the ioctl and movement handling funtions of the Haiku ps/2 alps driver. Packet descriptions based on FreeBSD 14.3 psm driver. Constant bit checks based on FreeBSD 14.3 psm driver. Packet processing code based on OpenBSD 7.8 pms driver with variable naming adjustments to match FreeBSD and Haiku. Change-Id: I42b62ccbb410060cceba4f17d299c7daf0c38b66 Reviewed-on: https://review.haiku-os.org/c/haiku/+/9956 Tested-by: Commit checker robot Reviewed-by: Adrien Destugues --- .../kernel/bus_managers/ps2/ps2_elantech.cpp | 104 ++++++++++++++++-- 1 file changed, 93 insertions(+), 11 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp index 86cf2456bf..06640fce40 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp @@ -99,6 +99,9 @@ elantech_process_packet_v3(elantech_cookie *cookie, touchpad_movement *_event, static status_t elantech_process_packet_v4(elantech_cookie *cookie, touchpad_movement *_event, uint8 packet[PS2_PACKET_ELANTECH]); +static status_t +elantech_process_packet_trackpoint(elantech_cookie *cookie, mouse_movement *_movement, + uint8 packet[PS2_PACKET_ELANTECH]); /* Common legend @@ -111,14 +114,18 @@ elantech_process_packet_v4(elantech_cookie *cookie, touchpad_movement *_event, * P: pressure */ static status_t -get_elantech_movement(elantech_cookie *cookie, touchpad_movement *_event, bigtime_t timeout) +get_elantech_movement(elantech_cookie *cookie, touchpad_read *_read) { + touchpad_movement event = {}; + mouse_movement movement {}; uint8 packet[PS2_PACKET_ELANTECH]; status_t status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, - timeout); - if (status < B_OK) + _read->timeout); + if (status < B_OK) { + TRACE("ELANTECH: acquire_sem_etc err or timeout.\n"); return status; + } if (!cookie->dev->active) { TRACE("ELANTECH: read_event: Error device no longer active\n"); @@ -132,25 +139,33 @@ get_elantech_movement(elantech_cookie *cookie, touchpad_movement *_event, bigtim } if ((cookie->capabilities[0] & 0x80) != 0 && (packet[3] & 0x0f) == 0x06) { - status = IGNORE_EVENT; + status = elantech_process_packet_trackpoint(cookie, &movement, packet); + if (status == B_OK) { + _read->event = MS_READ; + _read->u.mouse = movement; + } } else { switch (cookie->version) { case 1: - status = elantech_process_packet_v1(cookie, _event, packet); + status = elantech_process_packet_v1(cookie, &event, packet); break; case 2: - status = elantech_process_packet_v2(cookie, _event, packet); + status = elantech_process_packet_v2(cookie, &event, packet); break; case 3: - status = elantech_process_packet_v3(cookie, _event, packet); + status = elantech_process_packet_v3(cookie, &event, packet); break; case 4: - status = elantech_process_packet_v4(cookie, _event, packet); + status = elantech_process_packet_v4(cookie, &event, packet); break; default: TRACE("ELANTECH: Unknown version %d.\n", cookie->version); return B_ERROR; } + if (status == B_OK) { + _read->event = MS_READ_TOUCHPAD; + _read->u.touchpad = event; + } } return status; } @@ -785,6 +800,64 @@ elantech_process_packet_v4(elantech_cookie *cookie, touchpad_movement *_event, } +static status_t +elantech_process_packet_trackpoint(elantech_cookie *cookie, mouse_movement *_movement, + uint8 packet[PS2_PACKET_ELANTECH]) +{ + /* 7 6 5 4 3 2 1 0 (LSB) + * ------------------------------------------- + * ipacket[0]: 0 0 SY SX 0 M R L + * ipacket[1]: ~SX 0 0 0 0 0 0 0 + * ipacket[2]: ~SY 0 0 0 0 0 0 0 + * ipacket[3]: 0 0 ~SY ~SX 0 1 1 0 + * ipacket[4]: X7 X6 X5 X4 X3 X2 X1 X0 + * ipacket[5]: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * ------------------------------------------- + * X and Y are written in two's complement spread + * over 9 bits with SX/SY the relative top bit and + * X7..X0 and Y7..Y0 the lower bits. + */ + int32 x, y; + uint8 buttons; + + if (!(!(packet[0] & 0xC8) && !(packet[1] & 0x7F) + && !(packet[2] & 0x7F) && !(packet[3] & 0xC9) + && !(packet[0] & 0x10) != !(packet[1] & 0x80) + && !(packet[0] & 0x10) != !(packet[3] & 0x10) + && !(packet[0] & 0x20) != !(packet[2] & 0x80) + && !(packet[0] & 0x20) != !(packet[3] & 0x20))) + return IGNORE_EVENT; + + /* + * This firmware misreport coordinates for trackpoint + * occasionally. Discard packets outside of [-127, 127] range + * to prevent cursor jumps. + */ + if (packet[4] == 0x80 || packet[5] == 0x80 + || packet[1] >> 7 == packet[4] >> 7 + || packet[2] >> 7 == packet[5] >> 7) + return IGNORE_EVENT; + + x = packet[4] - 0x100 + (packet[1] << 1); + y = packet[5] - 0x100 + (packet[2] << 1); + + buttons = packet[0] & 7; + + mouse_movement movement = { + .buttons = buttons, + .xdelta = x, + .ydelta = y, + .timestamp = system_time(), + .wheel_ydelta = 0, + .wheel_xdelta = 0, + }; + + *_movement = movement; + + return B_OK; +} + + static status_t synaptics_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count) { @@ -1363,13 +1436,22 @@ elantech_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) return user_memcpy(buffer, &gHardwareSpecs, sizeof(gHardwareSpecs)); case MS_READ_TOUCHPAD: - TRACE("ELANTECH: MS_READ get event\n"); + TRACE("ELANTECH: MS_READ_TOUCHPAD get event\n"); if (user_memcpy(&read.timeout, &(((touchpad_read*)buffer)->timeout), sizeof(bigtime_t)) != B_OK) return B_BAD_ADDRESS; - if ((status = get_elantech_movement(cookie, &read.u.touchpad, read.timeout)) != B_OK) + if ((status = get_elantech_movement(cookie, &read)) != B_OK) { + TRACE("ELANTECH: ioctl error with status: %s\n", strerror(status)); return status; - read.event = MS_READ_TOUCHPAD; + } +#ifdef TRACE_PS2_ELANTECH + if (read.event = MS_READ_TOUCHPAD) { + TRACE("ELANTECH: ioctl touchpad fingers: 0x%x\n", (int)read.u.touchpad.fingers); + TRACE("ELANTECH: ioctl touchpad buttons: %d\n", (int)read.u.touchpad.buttons); + } else if (read.event = MS_READ) { + TRACE("ELANTECH: ioctl mouse buttons: %d\n", (int)read.u.mouse.buttons); + } +#endif return user_memcpy(buffer, &read, sizeof(read)); default: