ps/2 elantech: Initial cleanups and fixes to support input devices
- Add references for driver implementation.
References from FreeBSD 14.3, OpenBSD 7.8, Linux 6.17 sources
and Linux 4.16 documentation.
- Add pragma marks for easy navigation
- Add missing define to fetch firware info from hardware
- Reduce size of version field
- Update probe function to be able to enable supported devices
- Update open function to add missing v4 devices
- Fix enable_absolute_mode and use same snooze time as BSDs and Linux drivers
- Fix get_resolution_v4 and set sane defaults instead of failing with error
- Update license for ps2_elantech.{h,cpp}
Change-Id: I76fba3be04d7b940079fc78c3b7fe7d8bf8dfa06
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10011
Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
Diff
src/add-ons/kernel/bus_managers/ps2/ps2_elantech.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
src/add-ons/kernel/bus_managers/ps2/ps2_elantech.h | 15 ++++++++++++---
2 files changed, 73 insertions(+), 22 deletions(-)
@@ -1,12 +1,18 @@
/*
* Copyright 2013, Haiku, Inc.
* Copyright 2013-2025, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Hardware specs taken from the linux driver, thanks a lot!
* Based on ps2_alps.c
* Hardware specs taken from the linux and BSDs drivers, thanks a lot!
*
* References:
* - https:
* - https:
* - https:
* - https:
*
* Authors:
* Jérôme Duval <korli@users.berlios.de>
* Samuel Rodríguez Pérez <samuelrp84@gmail.com>
*/
@@ -26,7 +32,6 @@
#else
# define TRACE(x...)
#endif
const char* kElantechPath[4] = {
"input/touchpad/ps2/elantech_0",
@@ -41,13 +46,14 @@
#define ELANTECH_CMD_GET_CAPABILITIES 0x02
#define ELANTECH_CMD_GET_SAMPLE 0x03
#define ELANTECH_CMD_GET_RESOLUTION 0x04
#define ELANTECH_CMD_GET_ICBODY 0x05
#define ELANTECH_CMD_REGISTER_READ 0x10
#define ELANTECH_CMD_REGISTER_WRITE 0x11
#define ELANTECH_CMD_REGISTER_READWRITE 0x00
#define ELANTECH_CMD_PS2_CUSTOM_CMD 0xf8
#define EDGE_MOTION_WIDTH 55
@@ -94,7 +100,7 @@
TRACE("ELANTECH: bad crc buffer\n");
return B_ERROR;
}
uint8 packet_type = packet[3] & 3;
TRACE("ELANTECH: packet type %d\n", packet_type);
TRACE("ELANTECH: packet content 0x%02x%02x%02x%02x%02x%02x\n",
@@ -180,6 +186,9 @@
}
status_t
probe_elantech(ps2_dev* dev)
{
@@ -213,23 +222,53 @@
TRACE("ELANTECH: not found (4)\n");
return B_ERROR;
}
uint32 fwVersion = ((val[0] << 16) | (val[1] << 8) | val[2]);
bool v1 = fwVersion < 0x020030 || fwVersion == 0x020600;
bool v2 = (val[0] & 0xf) == 4 || ((val[0] & 0xf) == 2 && !v1);
bool v3 = (val[0] & 0xf) == 5;
bool v4 = (val[0] & 0xf) >= 6 && (val[0] & 0xf) <= 15;
if (val[0] == 0x0 || val[2] == 10 || val[2] == 20 || val[2] == 40
|| val[2] == 60 || val[2] == 80 || val[2] == 100 || val[2] == 200) {
TRACE("ELANTECH: not found (5)\n");
return B_ERROR;
}
bool supported = !v4 || !v3 || !v2 || !v1;
if (supported && v3) {
uint8 samples[3];
if (elantech_dev_send_command(dev, ELANTECH_CMD_GET_SAMPLE, samples, 3) != B_OK) {
TRACE("ELANTECH: failed to query sample data\n");
return B_ERROR;
}
if (samples[1] == 0x74) {
TRACE("ELANTECH: Absolute mode broken, forcing standard PS/2 protocol\n");
supported = false;
}
}
INFO("Elantech version %02X%02X%02X, under developement! Using fallback.\n",
val[0], val[1], val[2]);
if (supported) {
INFO("Elantech version %02X%02X%02X detected.\n",
val[0], val[1], val[2]);
} else {
INFO("Elantech version %02X%02X%02X, under developement! Using fallback.\n",
val[0], val[1], val[2]);
}
dev->name = kElantechPath[dev->idx];
dev->packet_size = PS2_PACKET_ELANTECH;
return B_ERROR;
return supported ? B_OK : B_ERROR;
}
static status_t
elantech_write_reg(elantech_cookie* cookie, uint8 reg, uint8 value)
{
@@ -339,8 +378,8 @@
if (elantech_dev_send_command(cookie->dev, ELANTECH_CMD_GET_RESOLUTION,
val, 3) != B_OK)
return B_ERROR;
*x = (val[1] & 0xf) * 10 + 790;
*y = ((val[1] & 0xf) >> 4) * 10 + 790;
*x = ((val[1] & 0xf) * 10 + 790) * 10 / 254;
*y = (((val[1] & 0xf) >> 4) * 10 + 790) * 10 / 254;
return B_OK;
}
@@ -416,21 +455,24 @@
}
if (cookie->version < 4) {
if (status == B_OK && cookie->version < 4) {
uint8 val;
for (uint8 retry = 0; retry < 5; retry++) {
status = elantech_read_reg(cookie, 0x10, &val);
if (status != B_OK)
break;
snooze(100);
snooze(2000);
}
}
return status;
}
status_t
elantech_open(const char *name, uint32 flags, void **_cookie)
{
@@ -502,8 +544,7 @@
case 5:
cookie->version = 3;
break;
case 6:
case 7:
case 6 ... 15:
cookie->version = 4;
break;
default:
@@ -541,9 +582,12 @@
"-%" B_PRIu32 " (%" B_PRIu32 ")\n", x_min, x_max, y_min, y_max, width);
uint32 x_res, y_res;
if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) {
TRACE("ELANTECH: get resolution failed!\n");
goto err4;
x_res = 31;
y_res = 31;
if (cookie->version == 4) {
if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) {
TRACE("ELANTECH: get resolution failed!\n");
}
}
TRACE("ELANTECH: resolution x %" B_PRIu32 " y %" B_PRIu32 " (dpi)\n",
@@ -1,13 +1,20 @@
/*
* Copyright 2013, Haiku, Inc.
* Copyright 2013-2025, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* The elantech_model_info struct and all the hardware specs are taken from the
* linux driver, thanks a lot!
* Hardware specs taken from the linux and BSDs drivers, thanks a lot!
*
* References:
* - https:
* - https:
* - https:
* - https:
*
* Authors:
* Jérôme Duval <korli@users.berlios.de>
* Samuel Rodríguez Pérez <samuelrp84@gmail.com>
*/
#ifndef _PS2_ELANTECH_H
#define _PS2_ELANTECH_H
@@ -25,7 +32,7 @@
struct packet_buffer* ring_buffer;
size_t packet_index;
uint32 version;
uint8 version;
uint32 fwVersion;
uint32 x;