elantp: Add support for more haptic hardware

Support ic_type 0x12 & 0x13, iap_ver 0x3 & 0x5 iap flow.
This commit is contained in:
jinglewu 2022-11-08 19:32:56 +08:00 committed by GitHub
parent be8fb81103
commit f93f5ebffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 502 additions and 33 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;