mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-03 19:59:26 +00:00
elantp: Add support for more haptic hardware
Support ic_type 0x12 & 0x13, iap_ver 0x3 & 0x5 iap flow.
This commit is contained in:
parent
be8fb81103
commit
f93f5ebffa
@ -23,8 +23,10 @@
|
||||
#define ETP_CMD_I2C_OSM_VERSION 0x0103
|
||||
#define ETP_CMD_I2C_GET_HID_ID 0x0100
|
||||
#define ETP_CMD_I2C_IAP_TYPE 0x0304
|
||||
#define ETP_CMD_I2C_FW_PW 0x030E
|
||||
#define ETP_CMD_FORCE_ADDR 0x03AD
|
||||
|
||||
#define ETP_CMD_I2C_FLIM_TYPE_ENABLE 0x0104
|
||||
#define ETP_CMD_I2C_FORCE_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
|
||||
@ -40,15 +42,18 @@
|
||||
#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_IC13_IAPV5_PW 0x37CA
|
||||
|
||||
#define ETP_FW_FORCE_TYPE_ENABLE_BIT 0x1
|
||||
#define ETP_FW_EEPROM_ENABLE_BIT 0x2
|
||||
|
||||
#define ETP_I2C_IAP_TYPE_REG 0x0040
|
||||
|
||||
#define ETP_I2C_ENABLE_REPORT 0x0800
|
||||
|
||||
#define ETP_I2C_IAP_RESET 0xF0F0
|
||||
#define ETP_I2C_MAIN_MODE_ON (1 << 9)
|
||||
#define ETP_I2C_IAP_RESET 0xF0F0
|
||||
#define ETP_I2C_MAIN_MODE_ON (1 << 9)
|
||||
#define ETP_I2C_MAIN_MODE_ON2 (1 << 12)
|
||||
|
||||
#define ETP_I2C_IAP_REG_L 0x01
|
||||
#define ETP_I2C_IAP_REG_H 0x06
|
||||
|
@ -14,13 +14,20 @@
|
||||
struct _FuElantpFirmware {
|
||||
FuFirmwareClass parent_instance;
|
||||
guint16 module_id;
|
||||
guint16 ic_type;
|
||||
guint16 iap_addr;
|
||||
guint16 iap_ver;
|
||||
gboolean force_table_support;
|
||||
guint32 force_table_addr;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuElantpFirmware, fu_elantp_firmware, FU_TYPE_FIRMWARE)
|
||||
|
||||
/* firmware block update */
|
||||
#define ETP_IAP_START_ADDR_WRDS 0x0083
|
||||
#define ETP_IC_TYPE_ADDR_WRDS 0x0080
|
||||
#define ETP_IAP_VER_ADDR_WRDS 0x0082
|
||||
#define ETP_IAP_START_ADDR_WRDS 0x0083
|
||||
#define ETP_IAP_FORCETABLE_ADDR_V5 0x0085
|
||||
|
||||
const guint8 elantp_signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
|
||||
|
||||
@ -31,6 +38,13 @@ fu_elantp_firmware_get_module_id(FuElantpFirmware *self)
|
||||
return self->module_id;
|
||||
}
|
||||
|
||||
guint16
|
||||
fu_elantp_firmware_get_ic_type(FuElantpFirmware *self)
|
||||
{
|
||||
g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
|
||||
return self->ic_type;
|
||||
}
|
||||
|
||||
guint16
|
||||
fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self)
|
||||
{
|
||||
@ -38,6 +52,27 @@ fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self)
|
||||
return self->iap_addr;
|
||||
}
|
||||
|
||||
guint16
|
||||
fu_elantp_firmware_get_iap_ver(FuElantpFirmware *self)
|
||||
{
|
||||
g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
|
||||
return self->iap_ver;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_elantp_firmware_get_forcetable_support(FuElantpFirmware *self)
|
||||
{
|
||||
g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), FALSE);
|
||||
return self->force_table_support;
|
||||
}
|
||||
|
||||
guint32
|
||||
fu_elantp_firmware_get_forcetable_addr(FuElantpFirmware *self)
|
||||
{
|
||||
g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
|
||||
return self->force_table_addr;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_elantp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
|
||||
{
|
||||
@ -49,6 +84,7 @@ fu_elantp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbB
|
||||
static gboolean
|
||||
fu_elantp_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error)
|
||||
{
|
||||
FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
|
||||
gsize bufsz = g_bytes_get_size(fw);
|
||||
const guint8 *buf = g_bytes_get_data(fw, NULL);
|
||||
|
||||
@ -72,6 +108,28 @@ fu_elantp_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, G
|
||||
}
|
||||
}
|
||||
|
||||
if (self->force_table_addr != 0) {
|
||||
for (gsize i = 0; i < sizeof(elantp_signature); i++) {
|
||||
guint8 tmp = 0x0;
|
||||
if (!fu_memread_uint8_safe(buf,
|
||||
bufsz,
|
||||
self->force_table_addr - 1 -
|
||||
sizeof(elantp_signature) + i,
|
||||
&tmp,
|
||||
error))
|
||||
return FALSE;
|
||||
if (tmp != elantp_signature[i]) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"signature[%u] invalid: got 0x%2x, expected 0x%02x",
|
||||
(guint)i,
|
||||
tmp,
|
||||
elantp_signature[i]);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
@ -86,8 +144,10 @@ fu_elantp_firmware_parse(FuFirmware *firmware,
|
||||
FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
|
||||
gsize bufsz = 0;
|
||||
guint16 iap_addr_wrds;
|
||||
guint16 force_table_addr_wrds;
|
||||
guint16 module_id_wrds;
|
||||
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
|
||||
/* presumably in words */
|
||||
if (!fu_memread_uint16_safe(buf,
|
||||
@ -130,6 +190,50 @@ fu_elantp_firmware_parse(FuFirmware *firmware,
|
||||
G_LITTLE_ENDIAN,
|
||||
error))
|
||||
return FALSE;
|
||||
if (!fu_memread_uint16_safe(buf,
|
||||
bufsz,
|
||||
offset + ETP_IC_TYPE_ADDR_WRDS * 2,
|
||||
&self->ic_type,
|
||||
G_LITTLE_ENDIAN,
|
||||
error))
|
||||
return FALSE;
|
||||
if (!fu_memread_uint16_safe(buf,
|
||||
bufsz,
|
||||
offset + ETP_IAP_VER_ADDR_WRDS * 2,
|
||||
&self->iap_ver,
|
||||
G_LITTLE_ENDIAN,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (self->ic_type != 0x12 && self->ic_type != 0x13)
|
||||
return TRUE;
|
||||
|
||||
if (self->iap_ver <= 4) {
|
||||
if (!fu_memread_uint16_safe(buf,
|
||||
bufsz,
|
||||
offset + (self->iap_addr + 6),
|
||||
&force_table_addr_wrds,
|
||||
G_LITTLE_ENDIAN,
|
||||
&error_local)) {
|
||||
g_debug("forcetable address wrong: %s", error_local->message);
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
if (!fu_memread_uint16_safe(buf,
|
||||
bufsz,
|
||||
offset + ETP_IAP_FORCETABLE_ADDR_V5 * 2,
|
||||
&force_table_addr_wrds,
|
||||
G_LITTLE_ENDIAN,
|
||||
&error_local)) {
|
||||
g_debug("forcetable address wrong: %s", error_local->message);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (force_table_addr_wrds % 32 == 0) {
|
||||
self->force_table_addr = force_table_addr_wrds * 2;
|
||||
self->force_table_support = TRUE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
|
@ -16,4 +16,12 @@ fu_elantp_firmware_new(void);
|
||||
guint16
|
||||
fu_elantp_firmware_get_module_id(FuElantpFirmware *self);
|
||||
guint16
|
||||
fu_elantp_firmware_get_ic_type(FuElantpFirmware *self);
|
||||
guint16
|
||||
fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self);
|
||||
guint16
|
||||
fu_elantp_firmware_get_iap_ver(FuElantpFirmware *self);
|
||||
gboolean
|
||||
fu_elantp_firmware_get_forcetable_support(FuElantpFirmware *self);
|
||||
guint32
|
||||
fu_elantp_firmware_get_forcetable_addr(FuElantpFirmware *self);
|
||||
|
@ -19,11 +19,15 @@
|
||||
struct _FuElantpHidDevice {
|
||||
FuUdevDevice parent_instance;
|
||||
guint16 ic_page_count;
|
||||
guint16 ic_type;
|
||||
guint16 iap_type;
|
||||
guint16 iap_ctrl;
|
||||
guint16 iap_password;
|
||||
guint16 iap_ver;
|
||||
guint16 module_id;
|
||||
guint16 fw_page_size;
|
||||
gboolean force_table_support;
|
||||
guint32 force_table_addr;
|
||||
guint8 pattern;
|
||||
};
|
||||
|
||||
@ -152,6 +156,14 @@ fu_elantp_hid_device_ensure_iap_ctrl(FuElantpHidDevice *self, GError **error)
|
||||
self->iap_ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
|
||||
/* in bootloader mode? */
|
||||
if (self->force_table_support && self->iap_ver <= 5) {
|
||||
if ((self->iap_ctrl & ETP_I2C_MAIN_MODE_ON2) == 0)
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||
else
|
||||
fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((self->iap_ctrl & ETP_I2C_MAIN_MODE_ON) == 0)
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||
else
|
||||
@ -160,13 +172,47 @@ fu_elantp_hid_device_ensure_iap_ctrl(FuElantpHidDevice *self, GError **error)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_device_read_force_table_enable(FuElantpHidDevice *self, GError **error)
|
||||
{
|
||||
guint8 buf[2] = {0x0};
|
||||
guint16 value;
|
||||
|
||||
if (!fu_elantp_hid_device_read_cmd(self,
|
||||
ETP_CMD_I2C_FORCE_TYPE_ENABLE,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
error)) {
|
||||
g_prefix_error(error, "failed to read force type cmd: ");
|
||||
return FALSE;
|
||||
}
|
||||
value = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
if (value == 0xFFFF || value == ETP_CMD_I2C_FORCE_TYPE_ENABLE) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"forcetype cmd not supported");
|
||||
return FALSE;
|
||||
}
|
||||
if ((buf[0] & ETP_FW_FORCE_TYPE_ENABLE_BIT) == 0) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"force type table not supported");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
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,
|
||||
ETP_CMD_I2C_FORCE_TYPE_ENABLE,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
error)) {
|
||||
@ -174,12 +220,12 @@ fu_elantp_hid_device_read_hatpic_enable(FuElantpHidDevice *self, GError **error)
|
||||
return FALSE;
|
||||
}
|
||||
value = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
if (value == 0xFFFF || value == ETP_CMD_I2C_FLIM_TYPE_ENABLE) {
|
||||
if (value == 0xFFFF || value == ETP_CMD_I2C_FORCE_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 ||
|
||||
if ((buf[0] & ETP_FW_FORCE_TYPE_ENABLE_BIT) == 0 ||
|
||||
(buf[0] & ETP_FW_EEPROM_ENABLE_BIT) == 0) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
@ -192,19 +238,84 @@ fu_elantp_hid_device_read_hatpic_enable(FuElantpHidDevice *self, GError **error)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_device_get_forcetable_address(FuElantpHidDevice *self, GError **error)
|
||||
{
|
||||
guint8 buf[2] = {0x0};
|
||||
guint16 addr_wrds;
|
||||
|
||||
if (self->iap_ver == 0x3) {
|
||||
self->force_table_addr = 0xFF40 * 2;
|
||||
return TRUE;
|
||||
}
|
||||
if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_FORCE_ADDR, buf, sizeof(buf), error)) {
|
||||
g_prefix_error(error, "failed to read force table address cmd: ");
|
||||
return FALSE;
|
||||
}
|
||||
addr_wrds = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
if (addr_wrds % 32 != 0) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"illegal force table address (%x)",
|
||||
addr_wrds);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->force_table_addr = addr_wrds * 2;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_device_write_fw_password(FuElantpHidDevice *self,
|
||||
guint16 ic_type,
|
||||
guint16 iap_ver,
|
||||
GError **error)
|
||||
{
|
||||
guint8 buf[2] = {0x0};
|
||||
guint16 pw = ETP_I2C_IC13_IAPV5_PW;
|
||||
guint16 value;
|
||||
|
||||
if (iap_ver < 0x5 || ic_type != 0x13)
|
||||
return TRUE;
|
||||
|
||||
if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_FW_PW, pw, error)) {
|
||||
g_prefix_error(error, "failed to write fw password cmd: ");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_FW_PW, buf, sizeof(buf), error)) {
|
||||
g_prefix_error(error, "failed to read fw password cmd: ");
|
||||
return FALSE;
|
||||
}
|
||||
value = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
if (value != pw) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"can't set fw password got:%x",
|
||||
value);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_device_setup(FuDevice *device, GError **error)
|
||||
{
|
||||
FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device);
|
||||
FuUdevDevice *udev_device = FU_UDEV_DEVICE(device);
|
||||
guint16 fwver;
|
||||
guint16 iap_ver;
|
||||
guint16 tmp;
|
||||
guint8 buf[2] = {0x0};
|
||||
guint8 ic_type;
|
||||
g_autofree gchar *version_bl = NULL;
|
||||
g_autofree gchar *version = NULL;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autoptr(GError) error_forcetable = NULL;
|
||||
|
||||
/* get pattern */
|
||||
if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) {
|
||||
@ -236,11 +347,11 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
|
||||
return FALSE;
|
||||
}
|
||||
if (self->pattern >= 1) {
|
||||
iap_ver = buf[1];
|
||||
self->iap_ver = buf[1];
|
||||
} else {
|
||||
iap_ver = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
self->iap_ver = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
}
|
||||
version_bl = fu_version_from_uint16(iap_ver, FWUPD_VERSION_FORMAT_HEX);
|
||||
version_bl = fu_version_from_uint16(self->iap_ver, FWUPD_VERSION_FORMAT_HEX);
|
||||
fu_device_set_version_bootloader(device, version_bl);
|
||||
|
||||
/* get module ID */
|
||||
@ -276,13 +387,13 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
|
||||
g_prefix_error(error, "failed to read IC body: ");
|
||||
return FALSE;
|
||||
}
|
||||
ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF;
|
||||
self->ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF;
|
||||
} else {
|
||||
ic_type = (tmp >> 8) & 0xFF;
|
||||
self->ic_type = (tmp >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
/* define the extra instance IDs (ic_type + module_id + driver) */
|
||||
fu_device_add_instance_u8(device, "ICTYPE", ic_type);
|
||||
fu_device_add_instance_u8(device, "ICTYPE", self->ic_type);
|
||||
fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", NULL);
|
||||
fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", "MOD", NULL);
|
||||
fu_device_add_instance_str(device, "DRIVER", "HID");
|
||||
@ -294,7 +405,7 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"no page count for ELANTP\\ICTYPE_%02X",
|
||||
ic_type);
|
||||
self->ic_type);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -305,6 +416,25 @@ fu_elantp_hid_device_setup(FuDevice *device, GError **error)
|
||||
if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error))
|
||||
return FALSE;
|
||||
|
||||
if (self->ic_type != 0x12 && self->ic_type != 0x13)
|
||||
return TRUE;
|
||||
|
||||
if (!fu_elantp_hid_device_read_force_table_enable(self, &error_forcetable)) {
|
||||
g_debug("no forcetable detected: %s", error_forcetable->message);
|
||||
} else {
|
||||
if (!fu_elantp_hid_device_get_forcetable_address(self, error)) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"get forcetable address fail");
|
||||
return FALSE;
|
||||
}
|
||||
self->force_table_support = TRUE;
|
||||
/* is in bootloader mode */
|
||||
if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!fu_elantp_hid_device_read_hatpic_enable(self, &error_local)) {
|
||||
g_debug("no haptic device detected: %s", error_local->message);
|
||||
} else {
|
||||
@ -323,6 +453,8 @@ fu_elantp_hid_device_prepare_firmware(FuDevice *device,
|
||||
{
|
||||
FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device);
|
||||
guint16 module_id;
|
||||
guint16 ic_type;
|
||||
gboolean force_table_support;
|
||||
g_autoptr(FuFirmware) firmware = fu_elantp_firmware_new();
|
||||
|
||||
/* check is compatible with hardware */
|
||||
@ -338,11 +470,123 @@ fu_elantp_hid_device_prepare_firmware(FuDevice *device,
|
||||
self->module_id);
|
||||
return NULL;
|
||||
}
|
||||
ic_type = fu_elantp_firmware_get_ic_type(FU_ELANTP_FIRMWARE(firmware));
|
||||
if (self->ic_type != ic_type) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"firmware ic type incompatible, got 0x%04x, expected 0x%04x",
|
||||
ic_type,
|
||||
self->ic_type);
|
||||
return NULL;
|
||||
}
|
||||
force_table_support =
|
||||
fu_elantp_firmware_get_forcetable_support(FU_ELANTP_FIRMWARE(firmware));
|
||||
if (self->force_table_support != force_table_support) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"firmware incompatible, forcetable incorrect.");
|
||||
return NULL;
|
||||
}
|
||||
if (self->force_table_support) {
|
||||
guint32 force_table_addr;
|
||||
guint32 diff_size;
|
||||
force_table_addr =
|
||||
fu_elantp_firmware_get_forcetable_addr(FU_ELANTP_FIRMWARE(firmware));
|
||||
if (self->force_table_addr < force_table_addr) {
|
||||
g_set_error(
|
||||
error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"firmware forcetable address incompatible, got 0x%04x, expected 0x%04x",
|
||||
force_table_addr / 2,
|
||||
self->force_table_addr / 2);
|
||||
return NULL;
|
||||
}
|
||||
diff_size = self->force_table_addr - force_table_addr;
|
||||
if (diff_size % 64 != 0) {
|
||||
g_set_error(
|
||||
error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"firmware forcetable address incompatible, got 0x%04x, expected 0x%04x",
|
||||
force_table_addr / 2,
|
||||
self->force_table_addr / 2);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* success */
|
||||
return g_steal_pointer(&firmware);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_device_filling_forcetable_firmware(FuDevice *device,
|
||||
guint8 *fw_data,
|
||||
gsize fw_size,
|
||||
guint32 force_table_addr,
|
||||
GError **error)
|
||||
{
|
||||
FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device);
|
||||
const guint8 fillature[] = {0x77, 0x33, 0x44, 0xaa};
|
||||
const guint8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
|
||||
guint8 buf[64] = {[0 ... 63] = 0xFF};
|
||||
guint16 block_checksum;
|
||||
guint16 filling_value;
|
||||
|
||||
if (self->force_table_addr == force_table_addr)
|
||||
return TRUE;
|
||||
|
||||
if (self->force_table_addr < force_table_addr) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"forcetable address wrong (%x,%x): ",
|
||||
force_table_addr,
|
||||
self->force_table_addr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0, /* dst */
|
||||
fillature,
|
||||
sizeof(fillature),
|
||||
0x0, /* src */
|
||||
sizeof(fillature),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
fu_memwrite_uint16(buf + 0x4, self->force_table_addr / 2, G_LITTLE_ENDIAN);
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
sizeof(buf) - 6, /* dst */
|
||||
signature,
|
||||
sizeof(signature),
|
||||
0x0, /* src */
|
||||
sizeof(signature),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
block_checksum = fu_sum16w(buf, sizeof(buf), G_LITTLE_ENDIAN) - 0xFFFF;
|
||||
filling_value = 0x10000 - (block_checksum & 0xFFFF);
|
||||
fu_memwrite_uint16(buf + 0x6, filling_value, G_LITTLE_ENDIAN);
|
||||
|
||||
for (guint i = force_table_addr; i < self->force_table_addr; i += 64) {
|
||||
if (!fu_memcpy_safe(fw_data,
|
||||
fw_size,
|
||||
i, /* dst */
|
||||
buf,
|
||||
sizeof(buf),
|
||||
0x0, /* src */
|
||||
sizeof(buf),
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_device_write_firmware(FuDevice *device,
|
||||
FuFirmware *firmware,
|
||||
@ -360,6 +604,7 @@ fu_elantp_hid_device_write_firmware(FuDevice *device,
|
||||
guint8 csum_buf[2] = {0x0};
|
||||
g_autoptr(GBytes) fw = NULL;
|
||||
g_autoptr(GPtrArray) chunks = NULL;
|
||||
guint total_pages;
|
||||
|
||||
/* progress */
|
||||
fu_progress_set_id(progress, G_STRLOC);
|
||||
@ -382,8 +627,56 @@ fu_elantp_hid_device_write_firmware(FuDevice *device,
|
||||
/* write each block */
|
||||
buf = g_bytes_get_data(fw, &bufsz);
|
||||
iap_addr = fu_elantp_firmware_get_iap_addr(firmware_elantp);
|
||||
chunks = fu_chunk_array_new(buf + iap_addr, bufsz - iap_addr, 0x0, 0x0, self->fw_page_size);
|
||||
for (guint i = 0; i < chunks->len; i++) {
|
||||
|
||||
if (self->force_table_support &&
|
||||
self->force_table_addr >=
|
||||
fu_elantp_firmware_get_forcetable_addr(FU_ELANTP_FIRMWARE(firmware))) {
|
||||
g_autofree guint8 *buf2 = g_malloc0(bufsz);
|
||||
if (!fu_memcpy_safe(buf2,
|
||||
bufsz,
|
||||
0x0, /* dst */
|
||||
buf,
|
||||
bufsz,
|
||||
0x0, /* src */
|
||||
bufsz,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (!fu_elantp_hid_device_filling_forcetable_firmware(
|
||||
device,
|
||||
buf2,
|
||||
bufsz,
|
||||
fu_elantp_firmware_get_forcetable_addr(FU_ELANTP_FIRMWARE(firmware)),
|
||||
error)) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_WRITE,
|
||||
"filling forcetable failed");
|
||||
return FALSE;
|
||||
}
|
||||
chunks = fu_chunk_array_new(buf2 + iap_addr,
|
||||
bufsz - iap_addr,
|
||||
0x0,
|
||||
0x0,
|
||||
self->fw_page_size);
|
||||
total_pages = (self->force_table_addr - iap_addr - 1) / self->fw_page_size + 1;
|
||||
if (total_pages > chunks->len) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_WRITE,
|
||||
"total pages wrong (%u)",
|
||||
total_pages);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
chunks = fu_chunk_array_new(buf + iap_addr,
|
||||
bufsz - iap_addr,
|
||||
0x0,
|
||||
0x0,
|
||||
self->fw_page_size);
|
||||
total_pages = chunks->len;
|
||||
}
|
||||
for (guint i = 0; i < total_pages; i++) {
|
||||
FuChunk *chk = g_ptr_array_index(chunks, i);
|
||||
guint16 csum_tmp =
|
||||
fu_sum16w(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN);
|
||||
@ -402,7 +695,6 @@ fu_elantp_hid_device_write_firmware(FuDevice *device,
|
||||
error))
|
||||
return FALSE;
|
||||
fu_memwrite_uint16(blk + fu_chunk_get_data_sz(chk) + 1, csum_tmp, G_LITTLE_ENDIAN);
|
||||
|
||||
if (!fu_elantp_hid_device_send_cmd(self, blk, blksz, NULL, 0, error))
|
||||
return FALSE;
|
||||
g_usleep(self->fw_page_size == 512 ? ELANTP_DELAY_WRITE_BLOCK_512 * 1000
|
||||
@ -552,6 +844,8 @@ fu_elantp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **err
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fu_elantp_hid_device_write_fw_password(self, ic_type, iap_ver, error))
|
||||
return FALSE;
|
||||
if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_IAP, self->iap_password, error))
|
||||
return FALSE;
|
||||
g_usleep(ELANTP_DELAY_UNLOCK * 1000);
|
||||
|
@ -20,6 +20,8 @@ struct _FuElantpHidHapticDevice {
|
||||
guint16 ic_page_count;
|
||||
guint16 iap_type;
|
||||
guint16 tp_iap_ctrl;
|
||||
guint16 tp_iap_ver;
|
||||
guint16 tp_ic_type;
|
||||
guint16 iap_ctrl;
|
||||
guint16 iap_password;
|
||||
guint16 module_id;
|
||||
@ -157,6 +159,14 @@ fu_elantp_hid_haptic_device_ensure_iap_ctrl(FuDevice *parent,
|
||||
self->tp_iap_ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
|
||||
/* in bootloader mode? */
|
||||
if (self->tp_iap_ver <= 5) {
|
||||
if ((self->tp_iap_ctrl & ETP_I2C_MAIN_MODE_ON2) == 0)
|
||||
fu_device_add_flag(FU_DEVICE(parent), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||
else
|
||||
fu_device_remove_flag(FU_DEVICE(parent), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((self->tp_iap_ctrl & ETP_I2C_MAIN_MODE_ON) == 0)
|
||||
fu_device_add_flag(FU_DEVICE(parent), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||
else
|
||||
@ -201,7 +211,7 @@ fu_elantp_hid_haptic_device_get_hatpic_driver_ic(FuDevice *parent,
|
||||
guint8 buf[2] = {0x0};
|
||||
guint16 value;
|
||||
if (!fu_elantp_hid_haptic_device_read_cmd(parent,
|
||||
ETP_CMD_I2C_FLIM_TYPE_ENABLE,
|
||||
ETP_CMD_I2C_FORCE_TYPE_ENABLE,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
error)) {
|
||||
@ -209,7 +219,7 @@ fu_elantp_hid_haptic_device_get_hatpic_driver_ic(FuDevice *parent,
|
||||
return FALSE;
|
||||
}
|
||||
value = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
if (value == 0xFFFF || value == ETP_CMD_I2C_FLIM_TYPE_ENABLE) {
|
||||
if (value == 0xFFFF || value == ETP_CMD_I2C_FORCE_TYPE_ENABLE) {
|
||||
g_set_error_literal(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
@ -217,7 +227,7 @@ fu_elantp_hid_haptic_device_get_hatpic_driver_ic(FuDevice *parent,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((buf[0] & ETP_FW_FLIM_TYPE_ENABLE_BIT) == 0 ||
|
||||
if ((buf[0] & ETP_FW_FORCE_TYPE_ENABLE_BIT) == 0 ||
|
||||
(buf[0] & ETP_FW_EEPROM_ENABLE_BIT) == 0) {
|
||||
g_set_error_literal(error,
|
||||
G_IO_ERROR,
|
||||
@ -285,9 +295,51 @@ fu_elantp_hid_haptic_device_get_version(FuDevice *parent,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_elantp_hid_haptic_device_write_fw_password(FuDevice *parent,
|
||||
guint16 tp_ic_type,
|
||||
guint16 tp_iap_ver,
|
||||
GError **error)
|
||||
{
|
||||
guint8 buf[2] = {0x0};
|
||||
guint16 pw = ETP_I2C_IC13_IAPV5_PW;
|
||||
guint16 value;
|
||||
|
||||
if (tp_iap_ver < 0x5 || tp_ic_type != 0x13)
|
||||
return TRUE;
|
||||
|
||||
if (!fu_elantp_hid_haptic_device_write_cmd(parent, ETP_CMD_I2C_FW_PW, pw, error)) {
|
||||
g_prefix_error(error, "failed to write fw password cmd: ");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!fu_elantp_hid_haptic_device_read_cmd(parent,
|
||||
ETP_CMD_I2C_FW_PW,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
error)) {
|
||||
g_prefix_error(error, "failed to read fw password cmd: ");
|
||||
return FALSE;
|
||||
}
|
||||
value = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
if (value != pw) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"can't set fw password got:%x",
|
||||
value);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
guint16 checksum;
|
||||
guint16 iap_password;
|
||||
guint16 tp_iap_ver;
|
||||
guint16 tp_ic_type;
|
||||
} FuElantpHaptictpWaitFlashEEPROMChecksumHelper;
|
||||
|
||||
static gboolean
|
||||
@ -322,6 +374,11 @@ fu_elantp_hid_haptic_device_write_checksum_cb(FuDevice *parent, gpointer user_da
|
||||
value);
|
||||
return FALSE;
|
||||
}
|
||||
if (!fu_elantp_hid_haptic_device_write_fw_password(parent,
|
||||
helper->tp_ic_type,
|
||||
helper->tp_iap_ver,
|
||||
error))
|
||||
return FALSE;
|
||||
if (!fu_elantp_hid_haptic_device_write_cmd(parent,
|
||||
ETP_CMD_I2C_IAP,
|
||||
helper->iap_password,
|
||||
@ -788,6 +845,8 @@ fu_elantp_hid_haptic_device_write_firmware(FuDevice *device,
|
||||
|
||||
helper.checksum = checksum_device;
|
||||
helper.iap_password = self->iap_password;
|
||||
helper.tp_ic_type = self->tp_ic_type;
|
||||
helper.tp_iap_ver = self->tp_iap_ver;
|
||||
if (!fu_device_retry_full(FU_DEVICE(parent),
|
||||
fu_elantp_hid_haptic_device_write_checksum_cb,
|
||||
3,
|
||||
@ -831,8 +890,6 @@ fu_elantp_hid_haptic_device_detach(FuDevice *device, FuProgress *progress, GErro
|
||||
{
|
||||
FuElantpHidDevice *parent;
|
||||
FuElantpHidHapticDevice *self = FU_ELANTP_HID_HAPTIC_DEVICE(device);
|
||||
guint16 tp_iap_ver;
|
||||
guint16 tp_ic_type;
|
||||
guint8 buf[2] = {0x0};
|
||||
guint16 ctrl;
|
||||
guint16 tmp;
|
||||
@ -878,9 +935,9 @@ fu_elantp_hid_haptic_device_detach(FuDevice *device, FuProgress *progress, GErro
|
||||
g_prefix_error(error, "failed to read IC body: ");
|
||||
return FALSE;
|
||||
}
|
||||
tp_ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF;
|
||||
self->tp_ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF;
|
||||
} else
|
||||
tp_ic_type = (tmp >> 8) & 0xFF;
|
||||
self->tp_ic_type = (tmp >> 8) & 0xFF;
|
||||
|
||||
/* get IAP firmware version */
|
||||
if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent),
|
||||
@ -893,16 +950,17 @@ fu_elantp_hid_haptic_device_detach(FuDevice *device, FuProgress *progress, GErro
|
||||
return FALSE;
|
||||
}
|
||||
if (self->pattern >= 1)
|
||||
tp_iap_ver = buf[1];
|
||||
self->tp_iap_ver = buf[1];
|
||||
else
|
||||
tp_iap_ver = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
self->tp_iap_ver = fu_memread_uint16(buf, G_LITTLE_ENDIAN);
|
||||
|
||||
/* set the page size */
|
||||
self->fw_page_size = 64;
|
||||
if (tp_ic_type >= 0x10) {
|
||||
if (tp_iap_ver >= 1) {
|
||||
if (self->tp_ic_type >= 0x10) {
|
||||
if (self->tp_iap_ver >= 1) {
|
||||
/* set the IAP type, presumably some kind of ABI */
|
||||
if (tp_iap_ver >= 2 && (tp_ic_type == 0x14 || tp_ic_type == 0x15)) {
|
||||
if (self->tp_iap_ver >= 2 &&
|
||||
(self->tp_ic_type == 0x14 || self->tp_ic_type == 0x15)) {
|
||||
self->fw_page_size = 512;
|
||||
} else {
|
||||
self->fw_page_size = 128;
|
||||
|
Loading…
Reference in New Issue
Block a user