elantp: Add ELAN hapticpad support

Co-authored-by: jingle.wu <jingle.wu@emc.com.tw>
This commit is contained in:
Richard Hughes 2022-10-22 17:20:46 +01:00 committed by GitHub
parent 0f42a5a152
commit 8eca325ae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1307 additions and 1 deletions

View File

@ -24,6 +24,25 @@
#define ETP_CMD_I2C_GET_HID_ID 0x0100 #define ETP_CMD_I2C_GET_HID_ID 0x0100
#define ETP_CMD_I2C_IAP_TYPE 0x0304 #define ETP_CMD_I2C_IAP_TYPE 0x0304
#define ETP_CMD_I2C_FLIM_TYPE_ENABLE 0x0104
#define ETP_CMD_I2C_SET_EEPROM_CTRL 0x0321
#define ETP_CMD_I2C_GET_EEPROM_FW_VERSION 0x0710
#define ETP_CMD_I2C_GET_EEPROM_IAP_VERSION 0x0711
#define ETP_CMD_I2C_SET_EEPROM_ENTER_IAP 0x0607
#define ETP_CMD_I2C_SET_EEPROM_LEAVE_IAP 0x0606
#define ETP_CMD_I2C_SET_EEPROM_DATATYPE 0x0702
#define ETP_CMD_I2C_CALC_EEPROM_CHECKSUM 0x060F
#define ETP_CMD_I2C_READ_EEPROM_CHECKSUM 0x070A
#define ETP_CMD_I2C_HAPTIC_RESTART 0x0601
#define ETP_CMD_I2C_EEPROM_SETTING 0x0322
#define ETP_CMD_I2C_EEPROM_LONG_TRANS_ENABLE 0x4607
#define ETP_CMD_I2C_EEPROM_SETTING_INITIAL 0x0000
#define ETP_CMD_I2C_EEPROM_WRITE_INFOMATION 0x4600
#define ETP_CMD_I2C_EEPROM_WRITE_CHECKSUM 0x048B
#define ETP_FW_FLIM_TYPE_ENABLE_BIT 0x1
#define ETP_FW_EEPROM_ENABLE_BIT 0x2
#define ETP_I2C_IAP_TYPE_REG 0x0040 #define ETP_I2C_IAP_TYPE_REG 0x0040
#define ETP_I2C_ENABLE_REPORT 0x0800 #define ETP_I2C_ENABLE_REPORT 0x0800
@ -41,6 +60,7 @@
#define ELANTP_DELAY_COMPLETE 1200 /* ms */ #define ELANTP_DELAY_COMPLETE 1200 /* ms */
#define ELANTP_DELAY_RESET 30 /* ms */ #define ELANTP_DELAY_RESET 30 /* ms */
#define ELANTP_EEPROM_READ_DELAY 100 /* ms */
#define ELANTP_DELAY_UNLOCK 100 /* ms */ #define ELANTP_DELAY_UNLOCK 100 /* ms */
#define ELANTP_DELAY_WRITE_BLOCK 35 /* ms */ #define ELANTP_DELAY_WRITE_BLOCK 35 /* ms */
#define ELANTP_DELAY_WRITE_BLOCK_512 50 /* ms */ #define ELANTP_DELAY_WRITE_BLOCK_512 50 /* ms */

View File

@ -0,0 +1,131 @@
/*
* Copyright (C) 2022 Jingle Wu <jingle.wu@emc.com.tw>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-elantp-common.h"
#include "fu-elantp-haptic-firmware.h"
struct _FuElantpHapticFirmware {
FuFirmwareClass parent_instance;
guint16 driver_ic;
};
G_DEFINE_TYPE(FuElantpHapticFirmware, fu_elantp_haptic_firmware, FU_TYPE_FIRMWARE)
const guint8 elantp_haptic_signature_ictype02[] = {0xFF, 0x40, 0xA2, 0x5B};
guint16
fu_elantp_haptic_firmware_get_driver_ic(FuElantpHapticFirmware *self)
{
g_return_val_if_fail(FU_IS_ELANTP_HAPTIC_FIRMWARE(self), 0);
return self->driver_ic;
}
static void
fu_elantp_haptic_firmware_export(FuFirmware *firmware,
FuFirmwareExportFlags flags,
XbBuilderNode *bn)
{
FuElantpHapticFirmware *self = FU_ELANTP_HAPTIC_FIRMWARE(firmware);
fu_xmlb_builder_insert_kx(bn, "driver_ic", self->driver_ic);
}
static gboolean
fu_elantp_haptic_firmware_check_magic(FuFirmware *firmware,
GBytes *fw,
gsize offset,
GError **error)
{
gsize bufsz = g_bytes_get_size(fw);
const guint8 *buf = g_bytes_get_data(fw, NULL);
for (gsize i = 0; i < sizeof(elantp_haptic_signature_ictype02); i++) {
guint8 tmp = 0x0;
if (!fu_memread_uint8_safe(buf, bufsz, i + offset, &tmp, error))
return FALSE;
if (tmp != elantp_haptic_signature_ictype02[i]) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"signature[%u] invalid: got 0x%2x, expected 0x%02x",
(guint)i,
tmp,
elantp_haptic_signature_ictype02[i]);
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_elantp_haptic_firmware_parse(FuFirmware *firmware,
GBytes *fw,
gsize offset,
FwupdInstallFlags flags,
GError **error)
{
FuElantpHapticFirmware *self = FU_ELANTP_HAPTIC_FIRMWARE(firmware);
gsize bufsz = 0;
guint8 v_s = 0;
guint8 v_d = 0;
guint8 v_m = 0;
guint8 v_y = 0;
guint8 tmp = 0;
g_autofree gchar *version_str = NULL;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
g_return_val_if_fail(fw != NULL, FALSE);
if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x4, &tmp, error))
return FALSE;
v_m = tmp & 0xF;
v_s = (tmp & 0xF0) >> 4;
if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x5, &v_d, error))
return FALSE;
if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x6, &v_y, error))
return FALSE;
if (v_y == 0xFF || v_d == 0xFF || v_m == 0xF) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"bad firmware version %02d%02d%02d%02d",
v_y,
v_m,
v_d,
v_s);
return FALSE;
}
version_str = g_strdup_printf("%02d%02d%02d%02d", v_y, v_m, v_d, v_s);
fu_firmware_set_version(FU_FIRMWARE(self), version_str);
/* success */
self->driver_ic = 0x2;
return TRUE;
}
static void
fu_elantp_haptic_firmware_init(FuElantpHapticFirmware *self)
{
}
static void
fu_elantp_haptic_firmware_class_init(FuElantpHapticFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
klass_firmware->check_magic = fu_elantp_haptic_firmware_check_magic;
klass_firmware->parse = fu_elantp_haptic_firmware_parse;
klass_firmware->export = fu_elantp_haptic_firmware_export;
}
FuFirmware *
fu_elantp_haptic_firmware_new(void)
{
return FU_FIRMWARE(g_object_new(FU_TYPE_ELANTP_HAPTIC_FIRMWARE, NULL));
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 Jingle Wu <jingle.wu@emc.com.tw>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#define FU_TYPE_ELANTP_HAPTIC_FIRMWARE (fu_elantp_haptic_firmware_get_type())
G_DECLARE_FINAL_TYPE(FuElantpHapticFirmware,
fu_elantp_haptic_firmware,
FU,
ELANTP_HAPTIC_FIRMWARE,
FuFirmware)
FuFirmware *
fu_elantp_haptic_firmware_new(void);
guint16
fu_elantp_haptic_firmware_get_driver_ic(FuElantpHapticFirmware *self);

View File

@ -14,6 +14,7 @@
#include "fu-elantp-common.h" #include "fu-elantp-common.h"
#include "fu-elantp-firmware.h" #include "fu-elantp-firmware.h"
#include "fu-elantp-hid-device.h" #include "fu-elantp-hid-device.h"
#include "fu-elantp-hid-haptic-device.h"
struct _FuElantpHidDevice { struct _FuElantpHidDevice {
FuUdevDevice parent_instance; FuUdevDevice parent_instance;
@ -159,6 +160,38 @@ fu_elantp_hid_device_ensure_iap_ctrl(FuElantpHidDevice *self, GError **error)
return TRUE; return TRUE;
} }
static gboolean
fu_elantp_hid_device_read_hatpic_enable(FuElantpHidDevice *self, GError **error)
{
guint8 buf[2] = {0x0};
guint16 value;
if (!fu_elantp_hid_device_read_cmd(self,
ETP_CMD_I2C_FLIM_TYPE_ENABLE,
buf,
sizeof(buf),
error)) {
g_prefix_error(error, "failed to read haptic enable cmd: ");
return FALSE;
}
value = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
if (value == 0xFFFF || value == ETP_CMD_I2C_FLIM_TYPE_ENABLE) {
g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not hapticpad");
return FALSE;
}
if ((buf[0] & ETP_FW_FLIM_TYPE_ENABLE_BIT) == 0 ||
(buf[0] & ETP_FW_EEPROM_ENABLE_BIT) == 0) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"the haptic eeprom not supported");
return FALSE;
}
/* success */
return TRUE;
}
static gboolean static gboolean
fu_elantp_hid_device_setup(FuDevice *device, GError **error) fu_elantp_hid_device_setup(FuDevice *device, GError **error)
{ {
@ -171,6 +204,7 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
guint8 ic_type; guint8 ic_type;
g_autofree gchar *version_bl = NULL; g_autofree gchar *version_bl = NULL;
g_autofree gchar *version = NULL; g_autofree gchar *version = NULL;
g_autoptr(GError) error_local = NULL;
/* get pattern */ /* get pattern */
if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) { if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) {
@ -178,7 +212,7 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
return FALSE; return FALSE;
} }
tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
self->pattern = tmp != 0xffff ? (tmp & 0xff00) >> 8 : 0; self->pattern = tmp != 0xFFFF ? (tmp & 0xFF00) >> 8 : 0;
/* get current firmware version */ /* get current firmware version */
if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_FW_VERSION, buf, sizeof(buf), error)) { if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_FW_VERSION, buf, sizeof(buf), error)) {
@ -271,6 +305,12 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error))
return FALSE; return FALSE;
if (!fu_elantp_hid_device_read_hatpic_enable(self, &error_local)) {
g_debug("no haptic device detected: %s", error_local->message);
} else {
g_autoptr(FuElantpHidHapticDevice) cfg = fu_elantp_haptic_device_new(device);
fu_device_add_child(FU_DEVICE(device), FU_DEVICE(cfg));
}
/* success */ /* success */
return TRUE; return TRUE;
} }
@ -606,6 +646,7 @@ fu_elantp_hid_device_init(FuElantpHidDevice *self)
fu_device_set_summary(FU_DEVICE(self), "Touchpad"); fu_device_set_summary(FU_DEVICE(self), "Touchpad");
fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); fu_device_add_icon(FU_DEVICE(self), "input-touchpad");
fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp"); fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp");
fu_device_set_vendor(FU_DEVICE(self), "ELAN Microelectronics");
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX);
fu_device_set_priority(FU_DEVICE(self), 1); /* better than i2c */ fu_device_set_priority(FU_DEVICE(self), 1); /* better than i2c */
fu_udev_device_set_flags(FU_UDEV_DEVICE(self), fu_udev_device_set_flags(FU_UDEV_DEVICE(self),

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2022 Jingle Wu <jingle.wu@emc.com.tw>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#include "fu-elantp-hid-device.h"
#define FU_TYPE_ELANTP_HID_HAPTIC_DEVICE (fu_elantp_hid_haptic_device_get_type())
G_DECLARE_FINAL_TYPE(FuElantpHidHapticDevice,
fu_elantp_hid_haptic_device,
FU,
ELANTP_HID_HAPTIC_DEVICE,
FuUdevDevice)
FuElantpHidHapticDevice *
fu_elantp_haptic_device_new(FuDevice *device);

View File

@ -807,6 +807,7 @@ fu_elantp_i2c_device_init(FuElantpI2cDevice *self)
fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); fu_device_add_icon(FU_DEVICE(self), "input-touchpad");
fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp"); fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp");
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX);
fu_device_set_vendor(FU_DEVICE(self), "ELAN Microelectronics");
fu_udev_device_set_flags(FU_UDEV_DEVICE(self), fu_udev_device_set_flags(FU_UDEV_DEVICE(self),
FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE); FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE);
fu_device_register_private_flag(FU_DEVICE(self), fu_device_register_private_flag(FU_DEVICE(self),

View File

@ -6,8 +6,10 @@ plugin_builtin_elantp = static_library('fu_plugin_elantp',
sources: [ sources: [
'fu-elantp-plugin.c', 'fu-elantp-plugin.c',
'fu-elantp-firmware.c', # fuzzing 'fu-elantp-firmware.c', # fuzzing
'fu-elantp-haptic-firmware.c',
'fu-elantp-hid-device.c', 'fu-elantp-hid-device.c',
'fu-elantp-i2c-device.c', 'fu-elantp-i2c-device.c',
'fu-elantp-hid-haptic-device.c',
], ],
include_directories: plugin_incdirs, include_directories: plugin_incdirs,
c_args: [ c_args: [